Grails已经内置支持内容协商通过使用任意HTTP Accept
报头,一种明确格式请求参数或URI映射的扩展.
在你开始处理内容协商之前,你必须告诉Grails希望支持什么样的内容类型。 默认情况下,grails-app/conf/Config.groovy
内使用 grails.mime.types
设置来配置若干不同的内容类型 :
grails.mime.types = [ xml: ['text/xml', 'application/xml'], text: 'text-plain', js: 'text/javascript', rss: 'application/rss+xml', atom: 'application/atom+xml', css: 'text/css', cvs: 'text/csv', all: '*/*', json: 'text/json', html: ['text/html','application/xhtml+xml'] ]
上面的小块配置,允许Grails检查把包含 'text/xml' 或 'application/xml' 媒体类型的一个请求的格式只当做 'xml'看待,你可以添加你自己的类型通过简单的添加条目到 map中.
每个进入的HTTP请求都有个指定的Accept报头,它定义了什么样的媒体类型(或 mime 类型)客户端能"接受"。在老式浏览器中通常是 :
*/*
这意味着任何事物.不过在新生浏览器中,所有东西一起像这样发送更有用(一个FirefoxAccept
报头示例) :
text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Grails解析这个进入的格式,并添加一个property
给request对象,用于描叙首选的请求格式。对于上述示例下列的断言会通过 :
assert 'html' == request.format
为什么?这个text/html
媒体类型拥有最高"质量"等级0.9,因此,具有最高优先权。如前所述,假如你有一老式浏览器结果会稍微不同 :
assert 'all' == request.format
在这种情况下,'all'可能的格式会被客户端接受。为了处理来自控制器(Controllers)不同类型的请求,你可以使用withFormat方法,它的行为被当作switch表达式 :
import grails.converters.*class BookController { def books def list = { this.books = Book.list() withFormat { html bookList:books js { render "alert('hello')" } xml { render books as XML } } } }
当Grails只执行 html()
调用并且首选的格式是html
时会发生什么。它只是让 Grails寻找每个名为grails-app/views/books/list.html.gsp
或grails-app/views/books/list.gsp
的视图。如果格式是xml
,那么,闭包会被调用,XML响应会被渲染 .
我们怎样处理'all'格式?只需在withFormat
代码块中简单指定content-types,以便,无论你想要的哪个都会被首先执行。因此,在上面示例中的"all" 将触发html
处理 .
当使用
withFormat时确保它在控制器(controller)操作(action)中最后一个被调用,因为
withFormat
方法的返回值用来决定操作(action)下一步做什么.
如果请求头的内容跟你的不一致,通过指定一个format
的请求参数覆盖这个格式 :
/book/list?format=xml
你同样可以在URL Mappings定义中定义这个参数 :
"/book/list"(controller:"book", action:"list") { format = "xml" }
Grails同样可以通过URI扩展支持内容协商。例如,给定下列URI:
/book/list.xml
Grails将剔除扩展并映射到/book/list
作为替代,同时,基于这个扩展把内容格式化为xml
。这个行为是默认允许的,那么,假如你希望关闭它, 你必须把grails-app/conf/Config.groovy
下的grails.mime.file.extensions
属性设置为false
:
grails.mime.file.extensions = false
为了在一个综合测试中测试内容协商(参见 测试部分)你可以操作每个进入的请求包头 :
void testJavascriptOutput() { def controller = new TestController() controller.request.addHeader "Accept", "text/javascript, text/html, application/xml, text/xml, */*"controller.testAction() assertEquals "alert('hello')", controller.response.contentAsString }
或者你可以设置格式化参数来实现类似的效果:
void testJavascriptOutput() { def controller = new TestController() controller.params.format = 'js'controller.testAction() assertEquals "alert('hello')", controller.response.contentAsString }