有时,我们想在我们的RESTful API中在同一资源的输出中支持不同级别的详细信息。 例如,默认输出包含基本字段,而更详细的输出包含资源的所有字段。 然后,我们API的客户端可以选择是否需要默认或详细输出。 在Grails中实现此目的的方法之一是使用转换器命名的configuration。
Grails转换器(如JSON
和XML
)支持命名配置。 首先,我们需要向转换器注册一个命名配置。 然后,我们可以use
配置名称和带有语句的闭包来调用转换器的use
方法以生成输出。 闭包中的代码在命名配置的上下文中执行。
Grails中的默认渲染器(例如DefaultJsonRenderer
)具有名为namedConfiguration
的属性。 如果将属性设置为在已配置的命名配置的上下文中呈现输出,则渲染器将使用命名的配置。 让我们配置适当的渲染器并注册命名配置,以支持默认渲染器中的命名配置。
在我们的示例中,我们有一个带有某些属性的User
资源。 我们希望支持简短和详细信息输出,其中在结果格式中包含不同的属性。
首先,我们有资源:
// File: grails-app/domain/com/mrhaki/grails/User.groovy
package com.mrhaki.grails
import grails.rest.*
// Set URI for resource to /users.
// The first format in formats is the default
// format. So we could use short if we wanted
// the short compact format as default for this
// resources.
@Resource(uri = '/users', formats = ['json', 'short', 'details'])
class User {
String username
String lastname
String firstname
String email
static constraints = {
email email: true
lastname nullable: true
firstname nullable: true
}
}
接下来,我们在Bootstrap
为此资源注册两个命名配置:
// File: grails-app/conf/Bootstrap.groovy
class Bootstrap {
def init = { servletContext ->
...
JSON.createNamedConfig('details') {
it.registerObjectMarshaller(User) { User user ->
final String fullname = [user.firstname, user.lastname].join(' ')
final userMap = [
id : user.id,
username: user.username,
email : user.email,
]
if (fullname) {
userMap.fullname = fullname
}
userMap
}
// Add for other resources a marshaller within
// named configuration.
}
JSON.createNamedConfig('short') {
it.registerObjectMarshaller(User) { User user ->
final userMap = [
id : user.id,
username: user.username
]
userMap
}
// Add for other resources a marshaller within
// named configuration.
}
...
}
...
}
现在,我们必须注册自定义渲染器User
资源,如Spring组件resources
:
// File: grails-app/conf/spring/resources.groovy
import com.mrhaki.grails.User
import grails.rest.render.json.JsonRenderer
import org.codehaus.groovy.grails.web.mime.MimeType
beans = {
// Register JSON renderer for User resource with detailed output.
userDetailsRenderer(JsonRenderer, User) {
// Grails will compare the name of the MimeType
// to determine which renderer to use. So we
// use our own custom name here.
// The second argument, 'details', specifies the
// supported extension. We can now use
// the request parameter format=details to use
// this renderer for the User resource.
mimeTypes = [new MimeType('application/vnd.com.mrhaki.grails.details+json', 'details')]
// Here we specify the named configuration
// that must be used by an instance
// of this renderer. See Bootstrap.groovy
// for available registered named configuration.
namedConfiguration = 'details'
}
// Register second JSON renderer for User resource with compact output.
userShortRenderer(JsonRenderer, User) {
mimeTypes = [new MimeType('application/vnd.com.mrhaki.grails.short+json', 'short')]
// Named configuration is different for short
// renderer compared to details renderer.
namedConfiguration = 'short'
}
// Default JSON renderer as fallback.
userRenderer(JsonRenderer, User) {
mimeTypes = [new MimeType('application/json', 'json')]
}
}
我们在grails-app/conf/spring/resources.groovy
定义了一些新的mime类型,但我们还必须将它们添加到grails-app/conf/Config.groovy
文件中:
// File: grails-app/conf/spring/resources.groovy
...
grails.mime.types = [
...
short : ['application/vnd.com.mrhaki.grails.short+json', 'application/json'],
details : ['application/vnd.com.mrhaki.grails.details+json', 'application/json'],
]
...
现在我们的应用程序已准备就绪并已配置。 如果我们请求资源,我们主要依靠Grails内容协商来获取正确的渲染器以生成我们的输出。 Grails内容协商可以使用请求参数format
的值来找到正确的mime类型,然后找到正确的渲染器。 Grails也可以检查Accept
请求标头或URI扩展,但是对于我们的RESTful API,我们想使用format
请求参数。
如果我们以不同的格式调用资源,则会看到以下结果:
$ curl -X GET http://localhost:8080/rest-sample/users/1
{
"class": "com.mrhaki.grails.User",
"id": 1,
"email": "hubert@mrhaki.com",
"firstname": "Hubert",
"lastname": "Klein Ikkink",
"username": "mrhaki"
}
$ curl -X GET http://localhost:8080/rest-sample/users/1?format=short
{
"id": 1,
"username": "mrhaki"
}
$ curl -X GET http://localhost:8080/rest-sample/users/1?format=details
{
"id": 1,
"username": "mrhaki",
"email": "hubert@mrhaki.com",
"fullname": "Hubert Klein Ikkink"
}
用Grails 2.4.2编写的代码。