Как обрабатывать исключения в коде RESTful на основе JSON?
У меня есть приложение "программное обеспечение как услуга", которое использует JSON, переданный через API RESTful.
Проще говоря: каковы наилучшие методы для сбора и отчетности исключений при использовании RESTful API с обменом данными JSON?
Моя первая мысль заключалась в том, чтобы увидеть, что делает Rails, создавая эшафот, но это явно не так. Вот выдержка:
class MumblesController < ApplicationController
# GET /mumbles/1
# GET /mumbles/1.json
def show
@mumble = Mumble.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: @mumble }
end
end
end
В этом случае, если код JSON отправляет несуществующий идентификатор, например
http://www.myhost.com/mumbles/99999.json
то Mumble.find() поднимет ActiveRecord:: RecordNotFound. ActionController поймает это и отобразит страницу с ошибкой в HTML. Но HTML бесполезен для клиента, ожидающего JSON.
Я мог бы обойти это, обернув Mumble.find() в блоке begin ... rescue RuntimeError
и предоставив статус JSON = > : unprocessable_entity или что-то в этом роде.
Но тогда, если клиентское приложение отправляет недопустимый путь, например:
http://www.myhost.com/badtypo/1.json
Является ли приложение на основе JSON предполагаемым для этого и возвращает ошибку в JSON? Если да, то где я могу его захватить, не углубляясь в ActionDispatch?
Итак, в общем, я punt и позволяю ActionController генерировать HTML, если есть ошибка? Это не кажется правильным...
Ответы
Ответ 1
(Я нашел ответ перед тем, как я ударил [Отправить свой вопрос]. Но это может помочь и другому...)
Ответ заключается в использовании ActionController rescue_from
, как описано в этом руководстве и документально подтвержденном . В частности, вы можете заменить рендеринг по умолчанию файлов 404.html и 500.html по умолчанию:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
private
def record_not_found(error)
render :json => {:error => error.message}, :status => :not_found
end
end
Ответ 2
Если это помогает кому-то, это то, что я сделал, как поймать все для моей чисто json api:
В вашем ApplicationController
, который наследуется каждым конкретным контроллером, добавьте
# app/controllers/api/v1/application_controller.rb
# ...
rescue_from StandardError do |exception|
render json: { :error => exception.message }, :status => 500
end
# ...
- основывается главным образом на бесстрашном ответе
Ответ 3
Как разработчик, вы также захотите увидеть следы (желательно с полезными линиями, отфильтровывая драгоценные камни). И сделать следы невидимыми для производства:
rescue_from StandardError do |exception|
# Handle only JSON requests
raise unless request.format.json?
err = {error: exception.message}
err[:backtrace] = exception.backtrace.select do |line|
# filter out non-significant lines:
%w(/gems/ /rubygems/ /lib/ruby/).all? do |litter|
not line.include?(litter)
end
end if Rails.env.development? and exception.is_a? Exception
# duplicate exception output to console:
STDERR.puts ['ERROR:', err[:error], '']
.concat(err[:backtrace] || []).join "\n"
render :json => err, :status => 500
end
Ответ 4
Нет четкого консенсуса относительно того, как сохранить согласованный стандарт для написания кода JSON API, но это часть того, что я тренирую (больше, чем вы просили):
- Держите это просто - постарайтесь оставаться спокойным. Пользовательские методы могут сделать вещи сложными быстро.
- Попросите сервер вернуть собственные коды ошибок и используйте "rescue_from" для захвата и
- в других случаях отображать Rails-коды ответа HTTP, которые могут быть специально нацелены на клиентское приложение.
В вашем случае вы можете найти Rails reply_to и reply_with обрабатывать html/json/другие ответы с грацией. И даже в вашем решении он по-прежнему будет эффективно отображать HTML, но это не то, что будет интерпретировано вашим клиентским приложением, которое вместо этого будет читать HTTP-заголовок и получить код ответа HTTP, что и запускает ваш "rescue_from",.