rest spring_带有Spring的REST的ETag

rest spring

1.概述

本文将重点介绍ETag – Spring支持,RESTful API的集成测试以及带有curl的使用场景。 这是关于使用Spring 3.1和Spring Security 3.1和基于Java的配置来建立安全的RESTful Web服务的系列文章的第9篇。

REST with Spring系列:

2. REST和ETag

从有关ETag支持的Spring官方文档中:

ETag (实体标签)是HTTP / 1.1兼容的Web服务器返回的HTTP响应标头,用于确定给定URL的内容更改。

ETag用于两件事–缓存和条件请求。 ETag值可以作为从Response主体的字节中计算出的哈希值 。 因为可能使用加密哈希函数,所以即使是正文的最小修改也将极大地改变输出,从而改变ETag的值。 这仅适用于强大的ETag-该协议也提供了较弱的Etag

使用If- *标头会将标准GET请求转换为条件GET 。 与ETag一起使用的两个If- *标头是“ If-None-Match ”和“ If-Match ” –各自具有自己的语义,如本文稍后所述。

3.使用

涉及ETag的简单的Client-Server通信可以分为以下步骤:

首先 ,客户端进行REST API调用–响应包括要存储以供进一步使用的ETag标头:

curl -H 'Accept: application/json' -i http://localhost:8080/rest-sec/api/resources/1
HTTP/1.1 200 OK
ETag: 'f88dd058fe004909615a64f01be66a7'
Content-Type: application/json;charset=UTF-8
Content-Length: 52

–客户端对RESTful API发出的下一个请求包括带有上一步中的ETag值的If-None-Match请求标头; 如果服务器上的资源未更改,则响应将不包含任何正文,并且状态代码为304 –未修改

curl -H 'Accept: application/json' -H 'If-None-Match: 'f88dd058fe004909615a64f01be66a7''
 -i http://localhost:8080/rest-sec/api/resources/1
HTTP/1.1 304 Not Modified
ETag: 'f88dd058fe004909615a64f01be66a7'

现在 ,在再次检索资源之前,我们将通过执行更新来对其进行更改:

curl --user admin@fake.com:adminpass -H 'Content-Type: application/json' -i
  -X PUT --data '{ 'id':1, 'name':'newRoleName2', 'description':'theNewDescription' }'
http://localhost:8080/rest-sec/api/resources/1
HTTP/1.1 200 OK
ETag: 'd41d8cd98f00b204e9800998ecf8427e'
<strong>Content-Length: 0</strong>

最后 ,我们发出了最后一个请求以再次获取特权; 请记住,自上次检索以来已对其进行了更新,因此以前的ETag值将不再起作用-响应将包含新数据和新ETag,这些ETag可以再次存储以备后用:

curl -H 'Accept: application/json' -H 'If-None-Match: 'f88dd058fe004909615a64f01be66a7'' -i
http://localhost:8080/rest-sec/api/resources/1
HTTP/1.1 200 OK
ETag: '03cb37ca667706c68c0aad4cb04c3a211'
Content-Type: application/json;charset=UTF-8
Content-Length: 56

一切就在这里– ETags狂野地节省了带宽。

4. Spring对ETag的支持

对Spring的支持–在Spring中使用ETag非常容易设置,并且对应用程序完全透明 。 通过在web.xml中添加一个简单的Filter来启用该支持:

<filter>
   <filter-name>etagFilter</filter-name>
   <filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class>
</filter>
<filter-mapping>
   <filter-name>etagFilter</filter-name>
   <url-pattern>/api/*</url-pattern>
</filter-mapping>

筛选器映射到与RESTful API本身相同的URI模式。 从Spring 3.0开始,过滤器本身就是ETag功能的标准实现。

该实现是一个浅层实现-根据响应计算ETag,这将节省带宽,不能 节省 服务器性能 。 因此,将受益于ETag支持的请求仍将作为标准请求处理,消耗通常会消耗的任何资源(数据库连接等),并且只有在将其响应返回给客户端之前,ETag支持才会启动在。

届时,ETag将从响应主体中计算出来,并在资源本身上设置; 同样,如果在请求中设置了If-None-Match标头,则也会对其进行处理。

ETag机制的更深层实现可能会带来更大的好处-例如为缓存中的某些请求提供服务,而根本不必执行计算-但这种实现绝非像浅层方法那样简单,也不可插入在这里描述。

5.测试ETag

让我们开始简单–我们需要验证检索单个Resource的简单请求的响应是否实际上将返回“ ETag”标头:

@Test
public void givenResourceExists_whenRetrievingResource_thenEtagIsAlsoReturned() {
    // Given
    Resource existingResource = getApi().create(new Resource());
    String uriOfResource = baseUri + '/' + existingResource.getId();
    // When
    Response findOneResponse = RestAssured.given().
      header('Accept', 'application/json').get(uriOfResource);
    // Then
    assertNotNull(findOneResponse.getHeader(HttpHeaders.ETAG));
}

接下来我们验证ETag行为的幸福路径 –如果从服务器检索资源的请求使用正确的ETag值,则不再返回资源。

@Test
public void givenResourceWasRetrieved_whenRetrievingAgainWithEtag_thenNotModifiedReturned() {
    // Given
    T existingResource = getApi().create(createNewEntity());
    String uriOfResource = baseUri + '/' + existingResource.getId();
    Response findOneResponse = RestAssured.given().
      header('Accept', 'application/json').get(uriOfResource);
    String etagValue = findOneResponse.getHeader(HttpHeaders.ETAG);
    // When
    Response secondFindOneResponse= RestAssured.given().
      header('Accept', 'application/json').headers('If-None-Match', etagValue)
      .get(uriOfResource);
    // Then
    assertTrue(secondFindOneResponse.getStatusCode() == 304);
}

一步步:

  • 首先创建资源 ,然后再检索–存储ETag值以备将来使用
  • 发送新的检索请求,这次使用“ If-None-Match ”标题指定先前存储的ETag
  • 在第二个请求上,服务器仅返回304 Not Modified ,因为在两个检索操作之间资源本身确实没有被修改。

最后 ,我们验证在第一个和第二个检索请求之间更改资源的情况:

@Test
public void givenResourceWasRetrieved_whenRetrievingAgainWithEtag_thenNotModifiedReturned() {
    // Given
    T existingResource = getApi().create(createNewEntity());
    String uriOfResource = baseUri + '/' + existingResource.getId();
    Response findOneResponse = RestAssured.given().
      header('Accept', 'application/json').get(uriOfResource);
    String etagValue = findOneResponse.getHeader(HttpHeaders.ETAG);
    existingResource.setName(randomAlphabetic(6))
    getApi().update(existingResource.setName(randomString));
    // When
    Response secondFindOneResponse= RestAssured.given().
      header('Accept', 'application/json').headers('If-None-Match', etagValue)
      .get(uriOfResource);
    // Then
    assertTrue(secondFindOneResponse.getStatusCode() == 200);
}

一步步:

  • 首先创建资源 ,然后再检索–存储ETag值以备将来使用
  • 然后更新相同的资源
  • 发送新的检索请求,这次使用“ If-None-Match ”标题指定先前存储的ETag
  • 在第二个请求上,服务器将返回200 OK以及完整的Resource,因为ETag值不再正确,因为与此同时资源已更新

接下来 ,我们测试“ If-Match ”的行为– ShallowEtagHeaderFilter没有为If-Match HTTP标头提供开箱即用的支持(在此JIRA问题上进行了跟踪),因此以下测试应失败:

@Test
public void givenResourceExists_whenRetrievedWithIfMatchIncorrectEtag_then412IsReceived() {
    // Given
    T existingResource = getApi().create(createNewEntity());
    // When
    String uriOfResource = baseUri + '/' + existingResource.getId();
    Response findOneResponse = RestAssured.given().header('Accept', 'application/json').
      headers('If-Match', randomAlphabetic(8)).get(uriOfResource);
    // Then
    assertTrue(findOneResponse.getStatusCode() == 412);
}

一步步:

  • 首先创建资源
  • 然后使用“ If-Match ”标头检索资源,指定错误的ETag值-这是有条件的GET请求
  • 服务器应返回412 Precondition Failed

6. ETag很大

我们仅将ETag用于读取操作存在一个RFC,试图阐明实现方式应如何在写入操作中处理ETag –这不是标准的,但很有趣。

当然,ETag机制还有其他可能的用途,例如使用Spring 3.1乐观锁机制以及处理相关的“丢失更新问题”

使用ETag时,还要注意一些已知的潜在陷阱和警告

7.结论

本文仅介绍了Spring和ETags所能提供的功能。 要全面实现启用了ETag的RESTful服务,以及用于验证ETag行为的集成测试,请查看github项目

参考:来自badung博客的JCG合作伙伴 Eugen Paraschiv的Spring的ETags

翻译自: https://www.javacodegeeks.com/2013/01/etags-for-rest-with-spring.html

rest spring

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值