首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

RESTful Grails(2)

RESTful Grails(2)

用 Grails 实现 GETful Web 服务从 Grails 应用程序中获取 POX 的最快捷的方式就是导入 grails.converters.*包,然后添加一对新的闭包,如清单 1 所示:
清单 1. 简单的 XML 输出
1
2
3
4
5
6
7
8
9
10
11
12
13
import grails.converters.*

class AirportController{
def xmlList = {
   render Airport.list() as XML
}

def xmlShow = {
   render Airport.get(params.id) as XML
}
  
//... the rest of the controller
}




您在 “中见过了使用中的 grails.converters” 包。该包向您提供了非常简单的 JavaScript Object Notation(JSON)和 XML 输出支持。图 1 展示了调用 xmlList操作的结果:
图 1. 来自于 Grails 的默认 XML 输出虽然默认的 XML 输出很好调试,但您还是想稍微自定义一下格式。还好,render()方法给您提供了一个 Groovy MarkupBuilder,它允许您动态定义自定义 XML(参见 ,查看更多有关 MarkupBuilder的消息的链接)。清单 2 创建了一些自定义 XML 输出:
清单 2. 自定义 XML 输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def customXmlList = {
def list = Airport.list()
render(contentType:"text/xml"){
   airports{
     for(a in list){
       airport(id:a.id, iata:a.iata){
         "official-name"(a.name)
         city(a.city)
         state(a.state)
         country(a.country)
         location(latitude:a.lat, longitude:a.lng)
       }
     }        
   }
}
}




图 2 展示了输出结果:
图 2. 使用 Groovy MarkupBuilder的自定义 XML 输出注意源代码和 XML 输出之间的对应的紧密程度。您可以随意定义元素名称(airports、airport、city),无需顾及它们是否与类的真实字段名称对应。如果您想提供一个以连字符链接的元素名称的话(诸如 official-name),又或者想要添加名称空间支持的话,只要给元素名称加上引号就可以了。而属性(诸如 id和 iata)是用 Groovy 散列映射 语法定义的。要填充元素的正文,需要提供一个不带 :的值。
内容协商与 Accept报头创建一个返回数据的 HTML 和 XML 表示的单独闭包是很简单的,但如果想创建一个既可以返回 HTML 又可以返回 XML 表示的闭包的话,该怎么办呢。这也是可以实现的,这要多亏在 HTTP 请求中包含有 Accept报头。这个简单的元数据告诉服务器:“嗨,您对这个 URI 中的资源可能有不只一个资源表示 —我更喜欢这个。”
cURL是一个方便的开源命令行 HTTP 工具(参见 )。在命令行输入 curl http://localhost:9090/trip/airport/list,以此来模拟请求机场列表的浏览器请求。您应该会看到 HTML 响应展现在您的荧屏上。
现在,对请求做两处小小的变动。这回,代替 GET发出一个 HEAD请求。HEAD是一个标准 HTTP 方法,它仅仅返回响应的元数据,而不返回正文(您现在正在进行的调试的类型包含在 HTTP 规范中)。另外,将 cURL放置于 verbose模式,这样您就也能够看到请求元数据了,如清单 3 所示:
清单 3. 使用 cURL来调试 HTTP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ curl --request HEAD --verbose http://localhost:9090/trip/airport/list
* About to connect() to localhost port 9090 (#0)
*   Trying ::1... connected
* Connected to localhost (::1) port 9090 (#0)
> HEAD /trip/airport/list HTTP/1.1
> User-Agent: curl/7.16.3 (powerpc-apple-darwin9.0)
       libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
> Host: localhost:9090
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Language: en-US
< Content-Type: text/html; charset=utf-8
< Content-Length: 0
< Server: Jetty(6.1.4)
<
* Connection #0 to host localhost left intact
* Closing connection #0




注意请求中的 Accept报头。客户机要是提交 */*的话,就意味着:“返回什么样的格式都无所谓。我将接受任何内容。”
cURL允许您使用这个值来覆盖 --header参数。输入 curl --request HEAD --verbose --header Accept:text/xml http://localhost:9090/trip/airport/list,并验证 Accept报头正在请求 text/xml。这就是资源的 MIME 类型了。
那么,Grails 是如何响应服务器端的 Accept报头的呢?再向 AirportController添加一个闭包,如清单 4 所示:
清单 4. debugAccept操作
1
2
3
4
5
def debugAccept = {
def clientRequest = request.getHeader("accept")
def serverResponse = request.format
render "Client: ${clientRequest}\nServer: ${serverResponse}\n"   
}




清单 4 中的第一行从请求中检索出了 Accept报头。第二行展示了 Grails 如何转换请求和它将要发回的响应。
现在,使用 cURL来做相同的搜索,如清单 5 所示:
清单 5. 调试 cURL中的 Accept报头
1
2
3
4
5
6
7
$ curl  http://localhost:9090/trip/airport/debugAccept
Client: */*
Server: all

$ curl  --header Accept:text/xml http://localhost:9090/trip/airport/debugAccept
Client: text/xml
Server: xml




all和 xml值是哪来的呢?看一下 grails-app/conf/Config.groovy。在文件顶部,您应该看到了一个散列映射,它对所有的键都使用了简单名称(像 all和 xml这样的名称),而且所有的值都使用了与之对应的 MIME 类型。清单 6 展示了 grails.mime.types散列映射:
清单 6. Config.groovy 中的 grails.mime.types散列
1
2
3
4
5
6
7
8
9
10
11
12
13
grails.mime.types = [ html: ['text/html','application/xhtml+xml'],
                     xml: ['text/xml', 'application/xml'],
                     text: 'text-plain',
                     js: 'text/javascript',
                     rss: 'application/rss+xml',
                     atom: 'application/atom+xml',
                     css: 'text/css',
                     csv: 'text/csv',
                     all: '*/*',
                     json: ['application/json','text/json'],
                     form: 'application/x-www-form-urlencoded',
                     multipartForm: 'multipart/form-data'
                   ]




高级的内容协商典型的 Web 浏览器提供的 Accept报头要比您与 cURL一起使用的稍微复杂些。例如,Mac OS X 10.5.4 上的 Firefox 3.0.1 提供的 Accept报头大致是这样的:
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
它是一个用逗号隔开的列表,它带有可选的 q属性,用以支持 MIME 类型(q值 —quality 的缩写 —是 float值,范围是 0.0 到 1.0)。由于 application/xml被赋予了一个为 0.9 的 q值,所以与其他类型的数据相比,Firefox 更偏好 XML 数据。
下面是 Mac OS X 10.5.4 上的 Safari 3.1.2 版本提供的 accept 报头:
text/xml,application/xml,application/xhtml+xml,
text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5

text/htmlMIME 类型被赋予了一个为 0.9 的 q值,所以首选的输出类型是 HTML,0.8 时为 text/plain,0.5 时为 */*。
参见 ,查看更多关于服务器端内容协商的信息。

那么,现在您应该对内容协商有了更多的了解了,您可以将 withFormat块添加到 list操作,以此来依据请求中的 Accept报头返回合适的数据类型,如清单 7 所示:
清单 7. 在一个操作中使用 withFormat块
1
2
3
4
5
6
7
8
9
10
11
12
def list = {
if(!params.max) params.max = 10
def list = Airport.list(params)
withFormat{
   html{
     return [airportList:list]
   }
   xml{
     render list as XML
   }
}
}




每一个块的最后一行一定会是一个 render、return或者 redirect—与普通操作没什么不同。如果 Accept报头变成 “all”(*/*)的话,则会使用块中的第一个条目。
改变 cURL中的 Accept报头是不错,但是通过改变 URI 您还可以作一些测试工作。http://localhost:8080/trip/airport/list.xml 和 http://localhost:8080/trip/airport/list?format=xml 都可以用来显式地覆盖 Accept报头。随便试一下 cURL和各种 URI 值,确保 withFormat块能发挥预期作用。
如果想让这个行为成为 Grails 中的标准的话,不要忘记您可以输入 grails install-templates,并在 /src/templates 中编辑文件。
所有的基本构建块就位之后,最后一步就是将 GETful 接口转化成一个真正的 RESTful 接口。
返回列表