8

I have the next scenario in my UrlMappings.groovy:

"/user/$action?" (controller:"user")
"/admin/$action?" (controller:"user")

"500"(controller:"error", action:"show")
"404"(controller:"error", action:"show")

And I need to know on the errorController from which controller was thrown the exception (if any) that raises the error 500, and show diferent error pages for users and admin.

Any ideas?

Thanks in advance.

David Santamaria
  • 8,671
  • 7
  • 33
  • 43

5 Answers5

14

You can access the exception in your ErrorController via request.exception. The top level exception always points to the controller where it was thrown so you can find out the controller name with exception.className. Here's a very simple example.

class ErrorController {

    def show = {
      def exception = request.exception
      render(text: "Exception in ${exception?.className}", 
        contentType: "text/plain", encoding: "UTF-8")
    }
}
Aoi Karasu
  • 3,730
  • 3
  • 37
  • 61
9

Using request.getAttribute("exception") you'll have the exception at your hand. I'd take a look at all request attributes, maybe there's a direct reference to the originating controller.

UPDATE

The trick is that Grails wraps the thrown exception into a GrailsWrappedRuntimeException providing comfortable access to the code being responsible for the exception. Use the following snippet in your error controller:

import org.codehaus.groovy.grails.web.errors.GrailsWrappedRuntimeException
def action = {   
   def exception = request.getAttribute('exception')
   if (exception instanceof GrailsWrappedRuntimeException) {
       log.error "exception $exception.className, line $exception.lineNumber has throw $exception.cause"
   }
}
Stefan Armbruster
  • 39,465
  • 6
  • 87
  • 97
1

To show different "error 500" page, I think you could do the same way with Grails scaffolding:

First, we just need to specify the view in URL Mapping:

"500"(view: "/500")   // Point to 500.gsp

Then here's the "500" view code:

        <h1>Grails Runtime Exception</h1>

        <h2>Error Details</h2>

        <div class="message">
            <strong>Error ${request.'javax.servlet.error.status_code'}:</strong>
            ${request.'javax.servlet.error.message'.encodeAsHTML()}<br/>
            <strong>Servlet:</strong> ${request.'javax.servlet.error.servlet_name'}<br/>
            <strong>URI:</strong> ${request.'javax.servlet.error.request_uri'}<br/>
            <g:if test="${exception}">
                <strong>Exception Message:</strong> ${exception.message?.encodeAsHTML()} <br/>
                <strong>Caused by:</strong> ${exception.cause?.message?.encodeAsHTML()} <br/>
                <strong>Class:</strong> ${exception.className} <br/>
                <strong>At Line:</strong> [${exception.lineNumber}] <br/>
                <strong>Code Snippet:</strong><br/>

                <div class="snippet">
                    <g:each var="cs" in="${exception.codeSnippet}">
                        ${cs?.encodeAsHTML()}<br/>
                    </g:each>
                </div>
            </g:if>
        </div>
        <g:if test="${exception}">
            <h2>Stack Trace</h2>

            <div class="stack">
                <pre><g:each in="${exception.stackTraceLines}">${it.encodeAsHTML()}<br/></g:each></pre>
            </div>
        </g:if>

You can extract whatever information you need from the error & stacktrace (div class="stack").

You can make 2 different template for user & admin, then a g:if tag will decide which template needs including in the view.

Hoàng Long
  • 10,746
  • 20
  • 75
  • 124
  • Still don't know if the exception was raise in the AdminController or in the UserController, that is what I am interested in – David Santamaria Mar 31 '11 at 07:28
  • @David: The ${request.'javax.servlet.error.message'.encodeAsHTML()} often contains the name of the Controller, you can search the string "Controller" in it, got the first dot(".") before and get the sub string. Or you can throw your own exception. – Hoàng Long Mar 31 '11 at 08:20
  • @David: I think it will be best if you include @Hoàng Long when you comment, so that I'll get a notification – Hoàng Long Mar 31 '11 at 08:22
  • exception.className is the targeted variable bu the problem is how to get it from another controller that handles the exception page, NOT from a view. Cheers. – Aoi Karasu Mar 31 '11 at 08:56
  • @AOI Karasu: thanks for pointing out about exception.className, I don't notice that simple way :). Maybe my answer is not clear, but getting from controller or view is the same – Hoàng Long Mar 31 '11 at 09:08
0

You can access the Controller instance that was handling the bad request via the request attribute org.codehaus.groovy.grails.CONTROLLER e.g. (in GSP):

Controller: ${request['org.codehaus.groovy.grails.CONTROLLER']}

To get the name of the controller:

Controller name: ${request['org.codehaus.groovy.grails.CONTROLLER_NAME_ATTRIBUTE']}

I tested this in Grails 2.0 and 2.2 but I can't find it documented anywhere, so this may be different in different versions of Grails. To see all attributes available in your request, add the following to error.gsp:

${${request.findAll { true }}
Armand
  • 23,463
  • 20
  • 90
  • 119
0

I sometimes annotate my controllers with a description that I'd like to show in the error message:

// ControllerType is a custom annotation
@ControllerType(description= "this does foo bar")
class MainController {
    ...

With that in place, and based on the post from Aoi Karasu, here's how to extract information from the initial Controller:

class ErrorsController {
def index() {
    def initialController = request.exception?.className
    if (initialController) {
        def controller = grailsApplication.getArtefact("Controller", initialController).getReferenceInstance()
        render "Controller: ${initialController}, annotations ${controller.getClass().getDeclaredAnnotations()}"
        return
    }
    render 'no initial controller'
}

}

request.exception?.className combined with grailsApplication.getArtefact allows to retrieve the controller, from which you can, for example, extract annotations

Igor
  • 21
  • 1