通过事件使可发现性脱钩
可发现性作为Web层的一个单独方面或关注点 ,应与处理HTTP请求的控制器分离。 为此,Controller将触发所有需要对HTTP响应进行其他操作的操作的事件:
@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可被发现
如前一篇文章所述,创建新资源的操作应在响应的Location HTTP标头中返回该资源的URI。 :
@Component
class ResourceCreatedDiscoverabilityListener implements ApplicationListener< ResourceCreated >{@Overridepublic 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以获取该特定类型的所有资源:
@Component
class SingleResourceRetrievedDiscoverabilityListener implements ApplicationListener< SingleResourceRetrieved >{@Overridepublic 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 Web服务中的入口点–它是客户端首次使用API时与之联系的对象。 如果要始终考虑并实施HATEOAS约束,那么这是一个起点。 到目前为止,必须从根目录中发现系统的大多数主要URI,这一事实不足为奇。
这是从根本上提供可发现性的示例控制器方法:
@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,因此它们可能会发生变化 。 但是,事实并非如此,这是有充分理由的:首先,这不是Web的工作方式–客户将URI加为书签,并希望它们将来能够正常工作。 其次,客户端不必浏览API就可以直接到达某个状态。
取而代之的是,RESTful Web服务的所有URI都应被视为即兴URI ,而URI 不变 。 相反,可以使用API的版本控制来解决URI重组的问题。
可发现性警告
正如前几篇文章中的某些讨论所指出的那样,可发现性的首要目标是最大限度地减少文档使用或不使用文档,并让客户通过获得的响应来学习和理解如何使用API。 实际上,这不应该被视为遥不可及的理想-这是我们如何使用每个新网页- 无需任何文档 。 因此,如果该概念在REST上下文中存在更多问题,那么它必须是技术实施的问题,而不是是否可能的问题。
话虽这么说,从技术上讲,我们离一个完整的解决方案还差得很远–规范和框架支持仍在不断发展,因此,可能必须做出一些折衷。 然而,这些都是妥协,应视为妥协。
结论
本文介绍了在具有Spring MVC的RESTful服务的上下文中实现可发现性的某些特征,并从根本上涉及了可发现性的概念。 在接下来的文章中,我将重点介绍自定义链接关系和Atom发布协议。 同时,检查github项目 。
参考: Baeldung博客上我们JCG合作伙伴 Eugen Paraschiv的第5部分 : Spring的REST服务发现性 。
相关文章 :
- 使用Spring 3.1和基于Java的配置引导Web应用程序,第1部分
- 使用Spring 3.1和基于Java的配置构建RESTful Web服务,第2部分
- 使用Spring Security 3.1保护RESTful Web服务,第3部分
- RESTful Web服务可发现性,第4部分
- 使用Spring Security 3.1的RESTful服务进行基本身份验证和摘要身份验证,第6部分
- Spring&Quartz集成自定义注释
- Spring MVC拦截器示例
- 在运行时交换出Spring Bean配置
翻译自: https://www.javacodegeeks.com/2011/12/rest-service-discoverability-with.html