Reputation: 117
I have a Granite User
model with some validations. When someone makes a POST
request to users/new
, I'd like to return the validation errors (if any) as JSON. Currently, I have:
if user.errors.size > 0
halt env, status_code: 500, response: user.errors.to_json
end
But when I try to compile, I get:
in /usr/local/Cellar/crystal/0.26.1/src/json/to_json.cr:66: no overload
matches 'Granite::Error#to_json' with type JSON::Builder
Overloads are:
- Object#to_json(io : IO)
- Object#to_json()
each &.to_json(json)
^~~~~~~
Upvotes: 0
Views: 98
Reputation: 561
So the issue is that User#errors
is an Array(Granite::Error)
, i.e. an Array
holding Granite::Error
s. Unfortunately, it doesn't look like Granite::Error
implements the to_json(JSON::Builder)
method (i.e. the method to_json
taking a parameter of type JSON::Builder
), which Array#to_json
relies on (that snippet you see there is from the implementation of Array#to_json
which you can view on GitHub.).
I'd recommend building the JSON yourself using JSON.build
. This has the added side-effect of keeping the JSON that you respond with (which I imagine is being consumed by some client) is entirely in your control. If the developers of Granite were to change how they encode Granite::Error
s in JSON and you were using their to_json
method, the change wouldn't raise anything at compile time.
As a side-note, I'd recommend against using a status code of 500 to denote a validation error, as that is typically reserved for unexpected errors internal to your server. A 4xx error (e.g. 400 - Bad Request) would be more appropriate. As a second side-note, it would be more RESTful to have the POST
be made to the /users
endpoint, as opposed to /users/new
.
With these changes, here would be the resulting snippet:
if user.errors.size > 0
errors_as_json = JSON.build do |json|
json.array do
user.errors.each do |error|
json.string error.to_s
end
end
end
halt env, status_code: 400, response: errors_as_json
end
Upvotes: 1