The new(ish) support for
respond in Grails 2.3 is great, and saves us the hassle of specifying different render/response calls depending on the content type. Surpisingly it falters when it comes to returning exceptions and errors, for example
404 NOT FOUND. This post describes a simple workaround which provides a meaningful response to a JSON/XML request as well as a user-friendly rendered 404 exception for humans.
The obvious approach is to respond with the object:
The above works great for basic JSON/XML requests, as it returns a 404 status code and no content. The main problem is that it doesn’t render HTML responses, so a user is presented with a blank page. Not so nice.
The following will quite happily respond with the correct content type for a valid widget, but if the widget doesn’t exist a 404 HTML page and 404 status code are returned (even if you requested JSON/XML):
We’re almost there, but not quite. We can use the
respond method and manually pass back the HTTP status and the view:
Hey presto, 404 error pages for HTML users, and a blank 404 response for JSON/XML requests. Obviously the above assumes you have a view named
error404.gsp to display a pretty 404 error page.
It might be a bit nicer if our JSON and XML responses contained a similar amount of information, and if we could pass some contextual information to the view (for example the type of thing we couldn’t find). No problem, just define a map with our response objects and then define that same map as the view model, along with the status code and view:
Let’s break the 404 response down a little.
if(!widget)is a Groovy way of checking for null/empty values
false, but we’ve added a little more information by saying the widget couldn’t be found.
model as Object. This tells
modelfor the JSON and XML responses. The coercion is required to prevent an error because of ambiguous arguments.
[model: model, status: 404, view: 'error404'], which provides the view arguments so the correct 404 GSP is loaded. The
modelspecified here is the same as is used for the JSON/XML response, so the GSP has access to the same information to render to the user.
So there we have it: a simple way to
respond and please all our sources. It’s not quite elegant, and I’m going to see how easy it would be to roll this into the
respond method itself.
Is there a better way? Let me know in the comments!
Was this post useful? Why not help others find it by sharing on twitter. While you're there, get in touch and let me know what you think!