пятница, 8 июня 2012 г.

Формат ответа сервиса в зависимости от запроса

Бывает необходимость в том что сервис возвращает разные результаты (xml, json, html) по одному адресу в зависимости от запроса.
Grails позволяет реализовать данную функциональность 2-мя способами.

  • Filters. В данной реализации необходимо наличия content-type в заголовке запроса;
  • AfterInterceptor. Реализация данного подхода возможна  в случаях если у вас в Config.groovy включена опция grails.mime.file.extensions = true.

Рассмотрим оба подхода.

Первый способ реализовать данную задачу с использованием фильтров. (http://grails.org/doc/latest/guide/single.html#filters). Подход позволяет пользоваться всеми прелестями Filtres в Grails. Реализуется данный подход через Content-type, что обязывает всегда указывать Content-Type, Grails распарсивает то что приходит в запросе и помещает content-type  в переменную format, объекта request.

class DetectFormatFilters {

    def filters = {
       all(action: '*') {
           after = { Map model ->
               switch (request.format) {
                   case 'html': println 'return HTML'; break;
                   case 'json': println 'return JSON'; break;
                   case 'xml': println 'return XML'; break;
               }
           }
       }
    }
}
P.S.: Типы указываются в Grails в config.groovy :
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'
                   ]



Второй подход заключается в использовании интерсепротов внутри controller.
(http://grails.org/doc/latest/ref/Controllers/afterInterceptor.html)
Данный вариант позволяет использовать несколько вариантов запроса:
  • domain/Controller/action/.json, domain/Controller/action/.xml. Данный вариант работает в случае если настройка grails.mime.file.extensions установлена в  true;
  • domain/Controller/action/?format=json,  domain/Controller/action/?format=xml.
Необходимо в контролле добавить afterInterceptor,  он будет перехватывать результат action перед тем как сформировать ответ.

def afterInterceptor = {
       withFormat {
           html {println 'return HTML-1'}
           json {println 'return JSON-1'}
           js { println "alert('hello')-1" }
           xml { println 'return XML-1' }
       }
    }

withFormat позволяет перечислить типы ответа которые нам необходимы, если тип ответа не обнаружен в списке то будет взят первый пункт, рекомендуется первым пунктом указывать html.