<b>2.4 连通性</b>
rest的一个重要的特性就是连通性。web
link和hateoas以不同方式实现了rest式服务的联通性。
web link定义在ietfrfc 5988(web linking),是通过在http头中定义链接信息,以描述当前页面与链接页面之间的关系。web link是一种过渡型链接(transitional links)。jax-rs 2.0引入了javax.ws.rs.core.link类,用来处理web link的表述。
hateoas(hypermedia as the
engine of application state,超媒体作为应用程序状态引擎)。hateoas的形式是包含链接信息的超媒体文档,hateoas的核心是“引擎”,该引擎的目的是通过请求的响应实体将超媒体信息返回给客户端,超媒体信息可以告诉用户,如果接下来选择去往某个链接(或者链接列表中的某个链接),应用的状态就会如超媒体描述的那样发生转变。hateoas是一种结构型链接(structural links)。jersey2中可以使用xml实现hateoas的结构要求。
下面讲述在jersey中如何实现web link和hateoa这两种rest连通性实践方式。
阅读指南
本节示例源代码地址:https://github.com/feuyeux/jax-rs2-guide-ii/tree/master/2.simple-service-3。
相关包:com.example.link。
<b>2.4.1 过渡型链接</b>
web link通过使用http的头信息来传递操作链接,在jersey中使用javax.ws.rs.core.link类可以非常简洁地实现支持web
link的资源类,示例代码如下。
@path("weblink-resource")
public class weblinkresource {
@context
uriinfo
uriinfo;
@post
@produces(mediatype.application_xml)
@consumes({ mediatype.application_json,
mediatype.application_xml, mediatype.text_xml })
public response savebook(final book book) {
final long newid = system.nanotime();
book.setbookid(newid);
linkcache.map.put(newid, book);
//关注点1:通过uriinfo实例获取资源路径
final uribuilder ub = uriinfo.getabsolutepathbuilder();
final uri location = ub.path("" + newid).build();
//关注点2:通过模板获取资源路径
final string uritemplate =
"http://{host}:{port}/{path}/{param}";
final uri location2 = uribuilder.fromuri(uritemplate)
.resolvetemplate("host",
"localhost").resolvetemplate("port", "9998")
.resolvetemplate("path", "weblink-resource")
.resolvetemplate("param", newid).build();
//关注点3:通过模板方法获取资源路径
final uribuilder ub3 = uriinfo.getabsolutepathbuilder();
final uri location3 =
ub3.scheme("http").host("localhost").port(9998)
.path("weblink-resource").path("" + newid).build();
//关注点4:为响应实例添加路径信息
return response.created(location).link(location2, "view1")
.link(location3, "view2").entity(book).build();
}
}
在这段代码中,使用了3种方式构建uri实例。第一种方式是通过调用uriinfo实例的getabsolutepathbuilder()方法可以获取当前请求的绝对路径,然后基于此路径添加资源id信息,见关注点1;第二种方式是为uribuilder提供路径模板,然后链式调用resolvetemplate()方法传递并解析模板参数,最后通过uribuilder的build方法生成uri实例,见关注点2;第三种方式和第二种类似,不同的是模板信息被具体方法替代。最后,这3个与link相关的uri实例由response构建,作为返回值响应给客户端,见关注点4。
<b>2.4.2 结构型链接</b>
hateoas用以代替聚集数据并避免描述膨胀,通常使用
atom格式在实体字段中提供链接信息。本例使用xml格式来支持hateoas,折中的设计是在pojo中额外定义一个链接字段。支持hateoas的资源类示例如下。
@path("hateoas-resource")
public class hateoasresource {
uriinfo uriinfo;
@produces({ mediatype.application_xml })
@consumes({ mediatype.application_xml })
public bookwrapper savebook(final book book) {
final uri uri = ub.path("" +
newid).build();
bookwrapper b = new bookwrapper();
b.setbook(book);
//关注点2:将资源路径赋给资源实体
b.setlink(uri.tostring());
return b;
}
在这段代码中,uri实例由上下文uriinfo中获取的绝对路径和资源id组成,见关注点1;该链接信息被赋值到pojo实例的link属性中,以实现hateoas,见关注点2。
rest连通性的实践手段非常多,推荐读者从成熟的产品中学习其设计。如果有可能,这里推荐jenkins和rallydev两个敏捷开发中常用的平台,它们提供了比较舒适的连通性设计。比如在rallydev中,为一个测试用例结果添加测试用例属性(该属性是必输项),其内容并不是对应测试用例的实例,而是该测试用例的引用地址字符串。这样的设计不但减少了网络传输的负载,还方便在调试和维护时排错。