Grails提供了一种方便地将HTTP response code和一些默认页面映射起来的方法。就是在URLMappings.groovy文件中加入如下的配置,
映射到view:
static mappings = { "500"(view:"/errors/serverError") "404"(view:"/errors/notFound") }
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 响应映射
版权所有,转载请注明来源