У меня есть обычный интерфейс HTML и JSON API в моем приложении Rails. Теперь, если кто-то вызывает /api/not_existent_method.json
, он возвращает страницу HTML 404 по умолчанию. Есть ли способ изменить это на что-то вроде {"error": "not_found"}
, оставив исходную 404-страничку для наружного интерфейса HTML?
Необходимо вернуть JSON-форматированную ошибку 404 в Rails
Ответ 1
Друг указал мне на элегантное решение, которое обрабатывает не только 404, но и 500 ошибок. Фактически, он обрабатывает каждую ошибку. Ключ состоит в том, что каждая ошибка генерирует исключение, которое распространяется вверх через стек промежуточных сред стойки, пока не будет обработано одним из них. Если вам интересно узнать больше, вы можете смотреть этот отличный скринкаст. У Rails есть собственные обработчики для исключений, но вы можете переопределить их с помощью менее документированного параметра конфигурации exceptions_app
. Теперь вы можете написать свое собственное промежуточное программное обеспечение, или вы можете перенаправить ошибку обратно в рельсы, например:
# In your config/application.rb
config.exceptions_app = self.routes
Затем вам просто нужно сопоставить эти маршруты в config/routes.rb
:
get "/404" => "errors#not_found"
get "/500" => "errors#exception"
И тогда вы просто создаете контроллер для обработки этого.
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
Последнее, что нужно добавить: В среде разработки рельсы обычно не отображают 404 или 500 страниц, но вместо этого печатают обратную линию. Если вы хотите увидеть свой ErrorsController
в действии в режиме разработки, отключите материал backtrace в файле config/enviroments/development.rb
.
config.consider_all_requests_local = false
Ответ 2
Мне нравится создавать отдельный контроллер API, который устанавливает формат (json) и 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:
it 'should return 404' do
get "/api/route/specific/to/your/app/", format: :json
expect(response.status).to eq(404)
end
Ответ 3
Конечно, он будет выглядеть примерно так:
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
не является реальным именем исключения. Он будет отличаться от версии Rails и точного поведения, которое вы хотите. Довольно легко найти с помощью поиска Google.
Ответ 4
Попробуйте добавить в конец вашего routes.rb
:
match '*foo', :format => true, :constraints => {:format => :json}, :to => lambda {|env| [404, {}, ['{"error": "not_found"}']] }