Статьи

Grails Goodness: использование именованных конфигураций конвертера с рендерами по умолчанию

Иногда мы хотим поддерживать в нашем RESTful API другой уровень детализации при выводе для одного и того же ресурса. Например, вывод по умолчанию с основными полями и более подробный вывод со всеми полями для ресурса. Затем клиент нашего API может выбрать, нужен ли вывод по умолчанию или подробный вывод. Один из способов реализовать это в Grails — использовать конвертер с именем конфигурации.

Преобразователи Grails, такие как JSON и XML , поддерживают именованные конфигурации. Сначала нам нужно зарегистрировать именованную конфигурацию в конвертере. Затем мы можем вызвать метод использования конвертера с именем конфигурации и закрытием с операторами для генерации вывода. Код в замыкании выполняется в контексте именованной конфигурации.

Средства визуализации по умолчанию в Grails, например DefaultJsonRenderer , имеют свойство с namedConfiguration . Средство визуализации будет использовать именованную конфигурацию, если свойство установлено для вывода выходных данных в контексте настроенной именованной конфигурации. Давайте настроим соответствующие средства визуализации и зарегистрируем именованные конфигурации для поддержки именованной конфигурации в средствах визуализации по умолчанию.

В нашем примере у нас есть User ресурс с некоторыми свойствами. Мы хотим поддерживать короткие и подробные выходные данные, где различные свойства включены в результирующий формат.

Сначала у нас есть наш ресурс:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 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 :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 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 :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 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')]
    }
 
}

Мы определили несколько новых типов mime в grails-app/conf/spring/resources.groovy , но мы также должны добавить их в наш файл grails-app/conf/Config.groovy :

1
2
3
4
5
6
7
8
// 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 параметра запроса format чтобы найти правильный тип MIME, а затем правильный рендерер. Grails также может проверить заголовок запроса Accept или расширение URI, но для нашего RESTful API мы хотим использовать параметр запроса format .

Если мы вызываем наш ресурс в разных форматах, мы видим следующие результаты:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
$ curl -X GET http://localhost:8080/rest-sample/users/1
{
    "class": "com.mrhaki.grails.User",
    "id": 1,
    "email": "[email protected]",
    "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": "[email protected]",
    "fullname": "Hubert Klein Ikkink"
}

Код, написанный с помощью Grails 2.4.2.

Ссылка: Grails Good: использование именованных конфигураций конвертера с рендерами по умолчанию от нашего партнера JCG Хьюберта Иккинка в блоге JDriven .