Как узнать откуда была выброшена ошибка 500 (Grails)

У меня есть следующий сценарий в моем UrlMappings.groovy:

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

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

И мне нужно знать на errorController, из какого контроллера было выброшено исключение (если оно есть), которое вызывает ошибку 500, и показывать разные страницы ошибок для пользователей и администратора.

Любые идеи?

Заранее спасибо.


person David Santamaria    schedule 30.03.2011    source источник


Ответы (5)


Вы можете получить доступ к исключению в вашем ErrorController через request.exception. Исключение верхнего уровня всегда указывает на контроллер, в котором оно было создано, поэтому вы можете узнать имя контроллера с помощью exception.className. Вот очень простой пример.

class ErrorController {

    def show = {
      def exception = request.exception
      render(text: "Exception in ${exception?.className}", 
        contentType: "text/plain", encoding: "UTF-8")
    }
}
person Aoi Karasu    schedule 31.03.2011

Используя request.getAttribute("exception"), вы получите исключение. Я бы посмотрел на все атрибуты запроса, возможно, есть прямая ссылка на исходный контроллер.

ОБНОВЛЕНИЕ

Хитрость заключается в том, что Grails оборачивает созданное исключение в GrailsWrappedRuntimeException обеспечивает удобный доступ к коду, ответственному за исключение. Используйте следующий фрагмент в вашем контроллере ошибок:

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"
   }
}
person Stefan Armbruster    schedule 30.03.2011
comment
До сих пор не знаю, было ли исключение вызвано в AdminController или в UserController, это то, что меня интересует. - person David Santamaria; 31.03.2011
comment
request.getAttribute('exception').className должен это сделать. - person Stefan Armbruster; 02.04.2011

Чтобы показать другую страницу «ошибка 500», я думаю, вы могли бы сделать то же самое с скаффолдингом Grails:

Во-первых, нам просто нужно указать представление в URL Mapping:

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

Тогда вот код просмотра "500":

        <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>

Вы можете извлечь любую необходимую информацию из ошибки и трассировки стека (div class="stack").

Вы можете создать 2 разных шаблона для пользователя и администратора, тогда тег g:if решит, какой шаблон нужно включить в представление.

person Hoàng Long    schedule 31.03.2011
comment
До сих пор не знаю, было ли исключение вызвано в AdminController или в UserController, это то, что меня интересует. - person David Santamaria; 31.03.2011
comment
@David: ${request.'javax.servlet.error.message'.encodeAsHTML()} часто содержит имя контроллера, вы можете найти в нем строку Controller, получить первую точку (.) раньше и получить подстрока. Или вы можете создать собственное исключение. - person Hoàng Long; 31.03.2011
comment
@David: я думаю, будет лучше, если вы укажете @Hoàng Long, когда будете комментировать, чтобы я получил уведомление - person Hoàng Long; 31.03.2011
comment
exception.className — это целевая переменная, но проблема заключается в том, как получить ее из другого контроллера, который обрабатывает страницу исключения, а НЕ из представления. Ваше здоровье. - person Aoi Karasu; 31.03.2011
comment
@AOI Karasu: спасибо, что указали на exception.className, я не заметил такого простого способа :). Возможно, мой ответ неясен, но получение из контроллера или представления одинаково - person Hoàng Long; 31.03.2011

Вы можете получить доступ к экземпляру контроллера, который обрабатывал неверный запрос, через атрибут запроса org.codehaus.groovy.grails.CONTROLLER, например. (в ВСП):

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

Чтобы получить имя контроллера:

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

Я тестировал это в Grails 2.0 и 2.2, но нигде не могу найти документацию, поэтому в разных версиях Grails это может отличаться. Чтобы увидеть все атрибуты, доступные в вашем запросе, добавьте в error.gsp следующее:

${${request.findAll { true }}
person Armand    schedule 11.03.2013

Иногда я аннотирую свои контроллеры описанием, которое хотел бы показать в сообщении об ошибке:

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

Имея это в виду и основываясь на сообщении Аой Карасу, вот как можно извлечь информацию из исходного контроллера:

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 в сочетании с grailsApplication.getArtefact позволяет получить контроллер, из которого можно, например, извлечь аннотации

person Igor    schedule 19.11.2013