A prototypical Action Controller action using Active Record will:
save!
) on the modeldef create
@resource = Resource.new(resource_params)
if @resource.save
render json: @resource, status: 201
else
render json: @resource, status: 400
end
end
Failing to succeed means, specifically, that the validations failed, or the callback chain was halted. In the case of a call to save!
, ActiveRecord::RecordNotSaved
or ActiveRecord::RecordInvalid
are 'unexceptional' exceptions to be expected and handled.
Developers are required to differentiate between exceptions that are expected and exceptions that are truly exceptional. This differentiation can add difficulty when designing their own classes and model logic.
In the case of an HTTP API that is intrinsically resourceful, we could alter this flow and avoid this distinction without loss of functionality or ease of use. For a resourceful JSON API, in both cases of 'expected' failure, a client still expects a JSON response about the requested resource, including information about why the request could not be satisfied.
In a more loosely coupled paradigm, the ActiveRecord callback chain has been replaced by Interaction classes responsible for the interactions on and between models. The validation coordination has been moved to Mapper classes. In both classes, an exception indicating the record could not be mutated as requested could be raised. This exception acts as an indicator that the requested resource mutation is not valid is some way (and shouldn't be retried without modification).
def create
@resource = Resource.new(resource_params)
ResourceMapper.create(@resource)
render json: @resource, status: 201
rescue RecordInvalid
render json: @resource, status: 400
end
or
rescue_from RecordInvalid, with: :render_bad_resource
def create
@resource = Resource.new(resource_params)
ResourceMapper.create(@resource)
render json: @resource, status: 201
end
private
def render_bad_resource
render json: @resource, status: 400
end
The logic and behavior haven't been altered, but there is no intrinsic differentiation between 'exceptional' exceptions and 'expected' ones.