Grails HTTP response codes mapping(默认错误页面)bug的解决方法

来源:互联网 发布:球队外号 知乎 编辑:程序博客网 时间:2024/06/01 10:46

Grails提供了一种方便地将HTTP response code和一些默认页面映射起来的方法。就是在URLMappings.groovy文件中加入如下的配置,

映射到view:

static mappings = {   "500"(view:"/errors/serverError")   "404"(view:"/errors/notFound")}

或者,映射到action:

static mappings = {   "500"(controller:"errors", action:"serverError")   "404"(controller:"errors", action:"notFound")}


一.Bug描述

首先我们使用了sitemesh作为模板, layout中定义<g:pageProperty name="page.body"/>,具体页面中通过<content tag="body">填充。

最开始的时候,我们是采用了映射到view的方式,如果后台出exception,应该会把500映射的view显示出来。

一切工作正常。

直到我们引入了Spring Security,这时我们发现如果Spring Security里抛出一些exception,在render错误页面的时候,只会render模板中的内容,具体页面中的内容都没有render出来。 

看了代码,发现Grails中有两处代码处理exception。

org.codehaus.groovy.grails.web.errors.GrailsExceptionResolver

是大多数情况下用的,它的位置类似于filter的位置,如果controller,service之类出了exception,会被它处理。

org.codehaus.groovy.grails.web.servlet.ErrorHandlingServlet

是exception一直抛而没人处理,当抛到容器(tomcat,jetty之类)的时候,容器就会调用这个servlet来处理exception。

这两处exception处理代码的逻辑是类似的,都会引用URLMappings.groovy里的配置,然后render页面。

Sping Security是通过filter实现的,而且在GrailsExceptionResolver外层,所以Spring Security的exception会一直抛到容器,直到ErrorHandlingServlet处理它。所以我们发现的第一个bug是:GrailsExceptionResolver处理exception很正常,但是ErrorHandlingServlet在render view的时候只render了模板中的内容,没render具体页面中的内容。

二.解决方法

由于已经涉及到了sitemesh之类的代码,就没有仔细去看。当时自然而然想到的方法就是把view换成action。也即URLMappings.groovy里的http response code不再映射view,而是映射contoller, action,在action里再render view。

似乎一切都好了。

三.第二个Bug

测试了好一会都没问题,直到测试了一个POST请求后发现有点不对。问题出现了,所有GET请求都是对的,所有POST请求都没有render 500页面而是render了404页面。

这回仔细看了代码。

Grails里,如果要请求到的是action而不是view的话,那么的请求里一定会带有两个参数:controller和action(这两个参数有两种来源,form表单或者url,而且都是隐式的存在。比如form里的一个名为_action_update_的button,或者url里的/user/update)。

我们发现的第二个Bug就是,如果提交POST请求的action的是通过form的button传到后台的话,在GrailsExceptionResolver处理exception的时候,没有把action换为URLMappings.groovy对应500配置的那个action。

比如我提交了controller=user, action=update,出了exception,GrailsExceptionResolver按照配置文件去映射,把controller换成了errors,但是action没换成serverError而仍然是update,那当然找不到这个action,所以出了404错误。

四.终极解决方法

最终我找到了一种终极解决方法^_^:

1.把URLMappings.groovy的映射配置从action换回view

2.mapping的view里只包含如下内容,这样view里就不显示任何东西,而是做一个重定向

<% response.sendRedirect("/xxx/errors/exception") %>
3.重定向会发到ErrorsController的exception action, 在这个action里我们再render另外一个view,这个view里会包含完整的错误页面信息。


关键字:Grails错误页面, Grails HTTP 响应映射


版权所有,转载请注明来源