I have a "software as a service" app that uses JSON communicated via a RESTful API.
Simply stated: what are the best practices for capturing and reporting exceptions when using a RESTful API with JSON data interchange?
My first thought was to see what Rails does by generating a scaffold, but that's clearly not right. Here's an excerpt:
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
In this case, if the JSON code sends a non-existent ID, e.g.
http://www.myhost.com/mumbles/99999.json
then Mumble.find() will raise ActiveRecord::RecordNotFound. ActionController will catch that and render an error page in HTML. But HTML is useless to the client that is expecting JSON.
I could work around that by wrapping the Mumble.find() in a begin ... rescue RuntimeError
block and rendering a JSON status => :unprocessable_entity or something.
But then what if the client's app sends an invalid path, e.g.:
http://www.myhost.com/badtypo/1.json
Is a JSON based app supposed to catch that and return an error in JSON? If so, where do I capture that without digging deep into ActionDispatch?
So overall, do I punt and let ActionController generate HTML if there's an error? That doesn't feel right...
To deal with exceptions, the recommended practice is to follow the sequence outlined below: Determine whether the REST API request succeeded or failed, based on the HTTP status response code returned. If the REST API request failed and the response is application/json, serialize the model.
Altogether, the most common way is to use @ExceptionHandler on methods of @ControllerAdvice classes so that the exception handling will be applied globally or to a subset of controllers. ControllerAdvice is an annotation introduced in Spring 3.2, and as the name suggests, is “Advice” for multiple controllers.
The most basic way of returning an error message from a REST API is to use the @ResponseStatus annotation. We can add the error message in the annotation's reason field. Although we can only return a generic error message, we can specify exception-specific error messages.
(I found the answer just before I hit [Post your question]. But this might help someone else as well...)
rescue_from
The answer is to use ActionController's rescue_from
, as described in this Guide and documented here. In particular, you can replace the default rendering of the default 404.html and 500.html files along these lines:
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
If it helps anyone, this is what I did as a catch all for my purely json api:
In your ApplicationController
that each specific controller inherits from, add
# app/controllers/api/v1/application_controller.rb # ... rescue_from StandardError do |exception| render json: { :error => exception.message }, :status => 500 end # ...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With