Отделить Обнаружение через события
Обнаруживаемость как отдельный аспект или проблема веб-слоя должна быть отделена от контроллера, обрабатывающего HTTP-запрос. Для этого контроллер будет запускать события для всех действий, которые требуют дополнительных манипуляций с HTTP-ответом:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
@RequestMapping ( value = "admin/foo/{id}" ,method = RequestMethod.GET ) @ResponseBody public Foo get( @PathVariable ( "id" ) Long id, HttpServletRequest request, HttpServletResponse response ){ Foo resourceById = RestPreconditions.checkNotNull( this .service.getById( id ) ); this .eventPublisher.publishEvent ( new SingleResourceRetrieved( this , request, response ) ); return resourceById; } @RequestMapping ( value = "admin/foo" ,method = RequestMethod.POST ) @ResponseStatus ( HttpStatus.CREATED ) public void create( @RequestBody Foo resource, HttpServletRequest request, HttpServletResponse response ){ RestPreconditions.checkNotNullFromRequest( resource ); Long idOfCreatedResource = this .service.create( resource ); this .eventPublisher.publishEvent ( new ResourceCreated( this , request, response, idOfCreatedResource ) ); } |
Эти события могут затем обрабатываться любым числом отсоединенных слушателей , каждый из которых сосредоточен на своем конкретном случае, и каждый движется в направлении удовлетворения общего ограничения HATEOAS.
Кроме того, слушатели должны быть последними объектами в стеке вызовов, и прямой доступ к ним не требуется; как таковые они не являются публичными.
Сделать URI вновь созданного ресурса доступным для обнаружения
Как обсуждалось в предыдущем посте , операция создания нового ресурса должна возвращать URI этого ресурса в HTTP-заголовке Location ответа. :
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Component class ResourceCreatedDiscoverabilityListener implements ApplicationListener< ResourceCreated >{ @Override public void onApplicationEvent( ResourceCreated resourceCreatedEvent ){ Preconditions.checkNotNull( resourceCreatedEvent ); HttpServletRequest request = resourceCreatedEvent.getRequest(); HttpServletResponse response = resourceCreatedEvent.getResponse(); long idOfNewResource = resourceCreatedEvent.getIdOfNewResource(); this .addLinkHeaderOnResourceCreation( request, response, idOfNewResource ); } void addLinkHeaderOnResourceCreation ( HttpServletRequest request, HttpServletResponse response, long idOfNewResource ){ String requestUrl = request.getRequestURL().toString(); URI uri = new UriTemplate( "{requestUrl}/{idOfNewResource}" ) .expand( requestUrl, idOfNewResource ); response.setHeader( HttpHeaders.LOCATION, uri.toASCIIString() ); } } |
К сожалению, работа с объектами запросов и ответов низкого уровня неизбежна даже в Spring 3.1, потому что первоклассная поддержка для определения Location все еще находится в разработке .
Получить одного ресурса
Извлечение одного ресурса должно позволить клиенту обнаружить URI, чтобы получить все ресурсы этого конкретного типа:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
@Component class SingleResourceRetrievedDiscoverabilityListener implements ApplicationListener< SingleResourceRetrieved >{ @Override public void onApplicationEvent( SingleResourceRetrieved resourceRetrievedEvent ){ Preconditions.checkNotNull( resourceRetrievedEvent ); HttpServletRequest request = resourceRetrievedEvent.getRequest(); HttpServletResponse response = resourceRetrievedEvent.getResponse(); this .addLinkHeaderOnSingleResourceRetrieval( request, response ); } void addLinkHeaderOnSingleResourceRetrieval ( HttpServletRequest request, HttpServletResponse response ){ StringBuffer requestURL = request.getRequestURL(); int positionOfLastSlash = requestURL.lastIndexOf( "/" ); String uriForResourceCreation = requestURL.substring( 0 , positionOfLastSlash ); String linkHeaderValue = RESTURLUtil .createLinkHeader( uriForResourceCreation, "collection" ); response.addHeader( LINK_HEADER, linkHeaderValue ); } } |
Обратите внимание, что в семантике отношения ссылки используется тип отношения « коллекция », определенный и используемый в нескольких микроформатах , но еще не стандартизированный.
Заголовок Link является одним из наиболее часто используемых HTTP-заголовков для целей обнаружения. Из-за этого необходимы некоторые простые утилиты , чтобы упростить создание его значений на сервере и избежать появления сторонней библиотеки.
Обнаруживаемость в корне
Корень — это точка входа в веб-службу RESTful, с которой клиент вступает в контакт при первом использовании API. Если ограничение HATEOAS должно быть рассмотрено и реализовано повсеместно, то это место для начала. Тот факт, что большинство основных URI системы должны быть обнаружены из корня, не должен вызывать удивления.
Это пример метода контроллера для обеспечения возможности обнаружения в корне:
01
02
03
04
05
06
07
08
09
10
|
@RequestMapping ( value = "admin" ,method = RequestMethod.GET ) @ResponseStatus ( value = HttpStatus.NO_CONTENT ) public void adminRoot( HttpServletRequest request, final response ){ String rootUri = request.getRequestURL().toString(); URI fooUri = new UriTemplate( "{rootUri}/{resource}" ).expand( rootUri, "foo" ); String linkToFoo = RESTURIUtil.createLinkHeader ( fooUri.toASCIIString(), REL_COLLECTION ); response.addHeader( HttpConstants.LINK_HEADER, linkToFoo ); } |
Это, конечно, иллюстрация концепции, которая будет прочитана в контексте доказательства концепции RESTful обслуживания серии. В более сложной системе было бы намного больше ссылок, каждая со своей семантикой, определяемой типом отношения ссылки .
Обнаруживаемость не об изменении URI
Одна из наиболее распространенных ошибок, связанных с обнаруживаемостью, заключается в неправильном понимании того, что, поскольку теперь URI являются обнаруживаемыми, они могут быть изменены . Однако это просто не тот случай, и на то есть веская причина: во-первых, это не так, как работает сеть — клиенты будут отмечать URI и ожидать, что они будут работать в будущем. Во-вторых, клиенту не нужно переходить через API, чтобы перейти к определенному состоянию, которое могло быть достигнуто напрямую.
Вместо этого все URI веб-службы RESTful следует рассматривать как классные URI , а классные URI не меняются . Вместо этого управление версиями API можно использовать для решения проблемы реорганизации URI.
Предостережения Обнаруживаемости
Как утверждают некоторые дискуссии вокруг предыдущих статей, первая цель обнаружения заключается в том, чтобы минимально использовать документацию или вообще не использовать ее, а клиент научиться и понимать, как использовать API с помощью полученных ответов. Фактически, это не должно рассматриваться как такой надуманный идеал — это то, как мы потребляем каждую новую веб-страницу — без какой-либо документации . Таким образом, если концепция является более проблематичной в контексте REST, то это должно быть вопросом технической реализации, а не вопросом того, возможно ли это.
При этом технически мы все еще далеки от полностью работающего решения — спецификации и поддержка фреймворка все еще развиваются, и из-за этого, возможно, придется пойти на некоторые компромиссы; это, тем не менее, компромиссы, и их следует рассматривать как таковые.
Вывод
В этой статье рассказывается о реализации некоторых признаков обнаруживаемости в контексте службы RESTful с Spring MVC и затрагивается концепция обнаруживаемости в корне. В следующих статьях я остановлюсь на пользовательских отношениях ссылок и протоколе публикации Atom. В то же время, проверьте проект GitHub .
Ссылка: Обнаружение службы REST с помощью Spring, часть 5 от нашего партнера JCG Евгения Параскива в блоге baeldung .
Статьи по Теме :
- Начальная загрузка веб-приложения с помощью Spring 3.1 и конфигурации на основе Java, часть 1
- Создание веб-службы RESTful с использованием Spring 3.1 и конфигурации на основе Java, часть 2
- Защита веб-службы RESTful с помощью Spring Security 3.1, часть 3
- Обнаружение веб-службы RESTful, часть 4
- Базовая и дайджест-аутентификация для службы RESTful с Spring Security 3.1, часть 6
- Интеграция Spring & Quartz с пользовательской аннотацией
- Пример Spring MVC Interceptors
- Обмен конфигурации Spring Bean во время выполнения