Статьи

Обнаружение службы REST с помощью Spring, часть 5

Это пятая из серии статей о настройке защищенного веб-сервиса RESTful с использованием Spring 3.1 и Spring Security 3.1 с настройкой на основе Java. В предыдущей статье была представлена ​​концепция Discoverability для службы RESTful, HATEOAS, а затем несколько практических сценариев, основанных на тестах. В этой статье основное внимание будет уделено фактической реализации обнаруживаемости и удовлетворению ограничения HATEOAS в службе REST с использованием Spring 3.1.

Отделить Обнаружение через события

Обнаруживаемость как отдельный аспект или проблема веб-слоя должна быть отделена от контроллера, обрабатывающего 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 .

Статьи по Теме :