Etags与春天休息
上一次更改:2020年3月28日
1.概述
本文将重点关注在春天使用etags,对REST API和消费方案的集成测试卷曲。
进一步阅读:
2.休息和etags
从Etag支持的官方春季文档:
一个Etag.(实体标记)是由HTTP / 1.1兼容Web服务器返回的HTTP响应标题,用于确定给定URL的内容更改。我们可以使用etags进行两件事 - 缓存和条件要求。这Etag值可以被认为是哈希从响应体的字节中计算出来。因为服务可能使用加密散列函数,所以即使是身体的最小修改也会大大改变输出,从而使Etag的值变化。这对于强大的Etags来说只有如此 - 协议确实提供了一个弱reag.也是。
使用A.如果-*标题将标准GET请求转换为条件GET。他们俩如果-*使用Eteags的标题是“if-none-match“ 和 ”if-match.“ - 每个都有自己的语义,如本文后面讨论的。
3.客户端 - 服务器通信卷曲
我们可以打破一个简单的客户端 - 服务器通信,涉及etags进入步骤:
首先,客户端拨打API调用 -响应包括Etag标题将存储以供进一步使用:
curl-h“接受:application / json”-i http:// localhost:8080 / spring-boot-rest / foos / 1
HTTP / 1.1 200 OK ETAG:“F88DD058FE004909615A64F01BE66A7”内容类型:应用程序/ JSON; CHARSET = UTF-8内容长度:52
对于下一个请求,客户将包括if-none-match请求从上一步的etag值的标题。如果资源在服务器上没有更改,则响应将包含任何机身和状态代码304 - 未修改:
curl-h“接受:应用程序/ json”-h'if-none-mate:“f88dd058fe004909615A64f01be66a7”''-i http:// localhost:8080 / spring-boot-rest / foos / 1
HTTP / 1.1 304未修改ETAG:“F88DD058FE004909615A64F01BE66A7”
现在,在再次检索资源之前,让我们通过执行更新来更改它:
curl-h“content-type:application / json”-i -x put --data'{id“:1,”名称“:”transformers2“}'http:// localhost:8080 / spring启动休息/ foos / 1
HTTP / 1.1 200 OK ETAG:“D41D8CD98F00B204E9800998ECF8427E”内容长度:0
最后,我们发出最后一个请求来再次检索FOO。请记住,自从我们最后一次要求它以来我们已经更新了它,因此前一个Etag值不应该工作。响应将包含新的数据和新的ETAG,它可以存储以供进一步使用:
curl-h“接受:应用程序/ json”-h'if-none-mate:“f88dd058fe004909615A64f01be66a7”''-i http:// localhost:8080 / spring-boot-rest / foos / 1
HTTP / 1.1 200 OK ETAG:“03CB37CA667706C68C0AAD4CB04C3A211”Content-Type:Application / JSON; Charset = UTF-8内容长度:56
在那里,你有它 - 在野外和节省带宽中的etags。
4.春天的Etag支持
在弹簧支持上:在弹簧中使用Etag非常容易为应用程序设置并完全透明。我们可以通过添加简单来启用支持筛选在里面web.xml.:
etagfilter filter-name> org.springframework.web.filter.shallyetagheaderfilter filter-class> filter> etagfilter filter-name> / foos / * url-pattern> filter-mapping>
我们正在将过滤器映射到与RESTful API本身相同的URI模式。过滤器本身是Spring 3.0以来Etag功能的标准实现。
实施是一个浅薄的- 应用程序根据响应计算ETAG,这将节省带宽但不是服务器性能。
因此,将从ETAG支持中受益的请求仍将被处理为标准请求,消耗通常会消耗(数据库连接等)的任何资源,并且只有在将其返回返回到客户端之前,它将成为Etag支持踢在。
此时,Etag将被计算出响应机构并在资源本身上设置;此外,如果if-none-match标题已设置在请求上,也将处理。
更深入的实现Etag机制可能提供更大的好处 - 例如服务于缓存的一些请求,而不必在所有请求中执行 - 但实现最肯定不会像浅水一样简单,也不是可插拔的。这里描述。
4.1。基于Java的配置
让我们看看基于Java的配置如何看起来像宣布A.BrayseTagheaderFilter.在我们的春天上下文中豆:
@bean public bulthentagheaderfilter bulthentagheaderfilter(){返回new bearlyetagheaderfilter();}
请记住,如果我们需要提供进一步的过滤配置,我们可以申报一个filterRegistrationBean.实例:
@Bean Public FilterRegistrationBean BrenceeTagheaderFilter(){FilterRegistrameBean FilterRegistrationBean = New FilterRegistrationBean <>(新的BrandheTagheaderFilter());filterRegistrationBean.addurlPatterns(“/ foos / *”);filterRegistrationBean.setName(“EtagFilter”);返回filterRegistrationBean;}
最后,如果我们不使用春令生启动,我们可以使用筛选器使用抽象annotationconfigdispatcherservletinitializersgetservletfilters.方法。
4.2。使用响应的响应Etag()方法
这种方法是在弹簧框架4.1中引入的,我们可以使用它来控制单个端点检索的ETAG值。
例如,想象一下我们将版本化实体用作乐观锁定机制访问我们的数据库信息。
我们可以使用该版本本身作为Etag来指示该实体是否已被修改:
@getmapping(value =“/ {id} / custom-etag”)公共andertantentity findbyidwithcustometag(@pathvariable(“id”)final long id){// ... foo foo = ... return anallytity.ok().etag(long.tostring(foo.getversion())).body(foo);}
该服务将检索相应的304 - 未修改如果请求的条件标题匹配缓存数据,则陈述。
5.测试Etags.
让我们开始简单 -我们需要验证检索单个资源的简单请求的响应实际返回“Etag“标题:
@test public voidedresourcexists_whenretrievingresource_thenetagisalsoreturned(){//给定字符串uriofresource = createauri();//响应时findoderesponse = restassured.given()。标题(“接受”,“application / json”)。get(uriofresource);//然后assertnotnull(findoneresponse.getheader(“Etag”));}
下一个那我们验证了Etag行为的快乐路径。如果请求检索资源从服务器使用正确的Etag.值,然后服务器无法检索资源:
@test public voidedresourcewasretrired_whenretrievingagainwithetag_thennotmodified returned(){//给定字符串uriofresource = createauri();response findoderesponse = RestAseSuced.given()。标题(“接受”,“application / json”)。get(uriofresource);string etagvalue = findoneresponse.getheader(httpheaders.etag);//响应时replyCindOnerSponse = RestAseSuced.given()。标题(“接受”,“Application / JSON”)。标题(“if-none-match”,etagvalue).get(uriofresource);//然后asserttrue(secondfindoneresponse.getstatuscode()== 304);}
一步步:
- 我们创建和检索资源,储存这Etag.价值
- 寄回新检索请求,这次使用“if-none-match“标题指定了Etag.预先存储的值
- 在第二个请求上,服务器只是返回一个304未修改,因为资源本身确实没有在两个检索操作之间修改
最后,我们验证了第一个和第二个检索请求之间更改资源的情况:
@test public void给resourcewasretrievedthenmodized_whenretrievingagainwithetag_thenresourceisreturned(){//给定字符串uriofresource = createauri();response findoderesponse = RestAseSuced.given()。标题(“接受”,“application / json”)。get(uriofresource);string etagvalue = findoneresponse.getheader(httpheaders.etag);现有resource.setName(OrmantalPhetic(6));更新(现有资产源);//响应时replyCindOnerSponse = RestAseSuced.given()。标题(“接受”,“Application / JSON”)。标题(“if-none-match”,etagvalue).get(uriofresource);//然后asserttrue(seconfindoneresponse.getstatuscode()== 200);}
一步步:
- 我们首先创建和检索一个资源- 并储存Etag.进一步使用的价值
- 然后我们更新相同的更新资源
- 这次发送新的Get请求,“if-none-match“标题指定了Etag.我们以前存储过
- 在第二个请求上,服务器将返回一个200 OK.随着完整的资源,因为Etag.值得不再是正确的,因为我们在同时更新了资源
最后,最后一次测试 - 由于功能有,这是不起作用的尚未在春天实施- 是支持的支持if-match.HTTP标头:
@test public voidedresourcexists_whenretrivewithifmatchincorrectag_then412isreceived(){//给定t存在resideResource = getapi()。create(createNewentity());//当字符串uroofresource = baseUri +“/”+现有resource.getId();response findoderesponse = restassured.given()。标题(“接受”,“application / json”)。标题(“If-Match”,OrmanalObethet(8))。Get(UrioFresource);//然后asserttrue(findoneresponse.getstatuscode()== 412);}
一步步:
- 我们创建资源
- 然后使用“if-match.“标题指定不正确Etag.价值 - 这是一个条件获取请求
- 服务器应该返回一个412前提条件失败了
6. Etags很大
我们只使用了readags进行阅读操作。一个RFC存在试图澄清实现如何处理写入操作的ETAG - 这不是标准的,但是一个有趣的阅读。
当然存在Etag机制的其他可能用途,例如用于乐观的锁定机构以及处理相关的“失去更新问题”。
还有几个已知的潜在的陷阱和警告要注意使用Etags时。
7.结论
本文仅划伤了春季和enags可能的表面。
为了完整实现ETAG,支持RESTful Service,以及集成测试验证ETAG行为,请查看GitHub项目。