Here is my magic formula for this. Maybe there is a better way, but this one works and ensures that the same view 404 displays whether you generate 404 or grails inside it (for example, the controller was not found).
First create a View class that extends AbstractView:
class NotFoundView extends AbstractView { @Override protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) { response.sendError(HttpServletResponse.SC_NOT_FOUND) } }
Then create an error controller:
class ErrorController { def notFound = { return render(view: '/error/notFound') } }
Now create your error view in view / error / notFound.gsp:
<g:applyLayout name="main"> <!doctype html> <html> <head> <title>Oops! Not found!</title> </head> <body> <h1>Not Found</h1> <section id="page-body"> <p>Nothing was found at your URI!</p> </section> </body> </html> </g:applyLayout>
It is very important to use the <g: applyLayout> tag. If you use your layout, it will be displayed twice and it is nested.
Now for URL mapping:
"404"(controller: 'error', action: 'notFound')
Now you should all send 404 from your controller:
def myAction = { Thing thing = Thing.get(params.id) if (!thing) { return new ModelAndView(new NotFoundView()) } }
This approach also makes it easy to record 404, try to resolve it, and send 301 or whatever you do.
paradoxbomb
source share