2012-04-20 12 views
69

estoy teniendo una interfaz HTML normal y una API JSON en mi aplicación Rails. Ahora, si alguien llama al /api/not_existent_method.json, devuelve la página HTML 404 predeterminada. ¿Hay alguna manera de cambiar esto a algo como {"error": "not_found"} dejando intacta la página 404 original para la interfaz HTML?necesidad de volver de error en formato JSON, 404 en los carriles

Respuesta

103

Un amigo me señaló hacia una solución elegante que no sólo manejar 404 500 sino también errores. De hecho, maneja todos los errores. La clave es que cada error genera una excepción que se propaga hacia arriba a través de la pila de middlewares de rack hasta que es manejado por uno de ellos. Si está interesado en obtener más información, puede ver this excellent screencast. Rails tiene sus propios controladores para las excepciones, pero puede anularlos mediante la opción de configuración exceptions_app menos documentada. Ahora, usted puede escribir su propio middleware o puede enrutar el error de nuevo en los carriles, así:

# In your config/application.rb 
config.exceptions_app = self.routes 

A continuación, sólo tiene que relacionar estas rutas en su config/routes.rb:

get "/404" => "errors#not_found" 
get "/500" => "errors#exception" 

Y entonces solo crea un controlador para manejar esto.

class ErrorsController < ActionController::Base 
    def not_found 
    if env["REQUEST_PATH"] =~ /^\/api/ 
     render :json => {:error => "not-found"}.to_json, :status => 404 
    else 
     render :text => "404 Not found", :status => 404 # You can render your own template here 
    end 
    end 

    def exception 
    if env["REQUEST_PATH"] =~ /^\/api/ 
     render :json => {:error => "internal-server-error"}.to_json, :status => 500 
    else 
     render :text => "500 Internal Server Error", :status => 500 # You can render your own template here 
    end 
    end 
end 

Una última cosa que añadir: En el entorno de desarrollo, los carriles Normalamente no hace que las páginas 404 o 500, pero imprime una traza en su lugar. Si quiere ver su ErrorsController en acción en modo de desarrollo, deshabilite el material de rastreo en su archivo config/enviroments/development.rb.

config.consider_all_requests_local = false 
+4

Además, no olvide agregar el código de estado a los renders. De lo contrario, su cliente/navegador no sabrá que era un 404/500. render: text => "404 Not Found",: estado =>: not_found – arnab

+0

he editado mi respuesta. – iblue

+1

Diría que un bloque de respuesta es más universal en las funciones de representación: responde_to do | format | format.json {render json: {error: "no encontrado"}. To_json, estado: 404} format.html {render text: "404 No encontrado", estado: 404} final –

11

Claro, que se verá algo como esto:

class ApplicationController < ActionController::Base 
    rescue_from NotFoundException, :with => :not_found 
    ... 

    def not_found 
    respond_to do |format| 
     format.html { render :file => File.join(Rails.root, 'public', '404.html') } 
     format.json { render :text => '{"error": "not_found"}' } 
    end 
    end 
end 

NotFoundException es no el nombre real de la excepción. Variará con la versión de Rails y el comportamiento exacto que desee. Bastante fácil de encontrar con una búsqueda en Google.

+2

Gracias por la idea, pero Estoy usando Rails 3.2.2 donde cambió el manejo de excepciones. Esto ya no funcionará. – iblue

+0

@iblue Esto funciona bien en Rails 3.2+ y es la mejor solución. Consulte 'rescue_from' [docs] (http://apidock.com/rails/v3.2.3/ActiveSupport/Rescuable/ClassMethods/rescue_from) para obtener más información. – Intelekshual

+0

Funciona solo para errores 404, no para 500 errores. – iblue

4

tratar de poner al final de suroutes.rb:

match '*foo', :format => true, :constraints => {:format => :json}, :to => lambda {|env| [404, {}, ['{"error": "not_found"}']] } 
+0

Para cualquier otra persona que lea esto, 'match' en el archivo' Rails.application.routes' ahora requiere que especifique el método HTTP, a través del parámetro 'via:'. Esto también aparecerá como un error cuando intente utilizar la línea anterior. – sameers

13

me gusta crear un controlador de API que establece el formato JSON() y los métodos específicos de la API:

class ApiController < ApplicationController 
    respond_to :json 

    rescue_from ActiveRecord::RecordNotFound, with: :not_found 
    # Use Mongoid::Errors::DocumentNotFound with mongoid 

    def not_found 
    respond_with '{"error": "not_found"}', status: :not_found 
    end 
end 

RSpec prueba:

it 'should return 404' do 
    get "/api/route/specific/to/your/app/", format: :json 
    expect(response.status).to eq(404) 
    end 
+0

Esto parece funcionar solo para registros. ¿Cómo manejarías el caso de 'api/non_existant_route'? – Sebastialonso

+0

la línea 'rescue_from ActiveRecord :: RecordNotFound, with: not_found' debe ser' with:: not_found' pero solo es una edición de un carácter: P – erroric

+0

@erroric gracias! :) –