`
ajax
  • 浏览: 253665 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Restlet实战(二十三)实现条件GET (Conditional Get)

    博客分类:
  • REST
阅读更多

先普及一下什么是条件GET,以下摘自<<Restful Web Service>>:

 

条件HTTP GET(conditional HTTP GET)既能几声服务器带宽,又能节省客户端带宽,作用是避免服务器
重复向一个客户端发送相同的表示。它是通过两个响应报头(Last-Modified和ETag)和两个请求报头
(IfModified-Since和If-None-Match)实现的。

这是无法靠客户端或服务器单方面解决的,若客户端在获取一个表示后,不再与服务器联系,那么该客户
端将无法获知服务器上的表示是否有变化。由于服务器并不保存应用状态,所以它也无法知道一个客户端
上次获取某个表示发生在何时。HTTP不是一个可靠的协议,所以客户端第一次就没有收到表示是有可能的
。当客户端请求一个表示时,服务器并不知道客户端之前有没有申请过这个表示--除非客户端能够提供
相关信息(作为部分应用状态)。

条件HTTP GET需要由客户端和服务器共同参与完成。服务器发送表示时,应当设置一些响应报头:Last-
Modified或ETag。客户端重复请求一个表示时,也应当设置一些报头:IfModified-Since和If-None-
Match--服务器根据这些信息决定是否重新发送表示。

 

 

下面看看在Restlet中,如何实现条件GET。首先看服务器端代码:

 

@Override
public void handleGet() {
	Date modifiedSince = getRequest().getConditions().getUnmodifiedSince();

	SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	Date lastModifiedAtServer = null;
	try {
		lastModifiedAtServer = format.parse("2009-07-29 00:00:00");
	} catch (ParseException e) {
		e.printStackTrace();
	}

	if (modifiedSince == null
			|| modifiedSince.getTime() < lastModifiedAtServer.getTime()) {
		// get user data from database through user id
		User user = null;
		Representation representation = new StringRepresentation(
				getUserXml(user), MediaType.TEXT_PLAIN);
		representation.setMediaType(MediaType.TEXT_PLAIN);
		getResponse().setStatus(Status.SUCCESS_OK);
		getResponse().setEntity(representation);
	} else {
		getResponse().setStatus(Status.REDIRECTION_NOT_MODIFIED);
	}
}

 

客户端请求代码:

 

SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date lastModified = null;
try {
	lastModified = format.parse("2009-07-28 00:00:00");
} catch (ParseException e) {
	e.printStackTrace();
}
Reference reference = new Reference("http://localhost:8080/restlet/resources/users/1");
Request get = new Request(Method.GET, reference);
get.getConditions().setUnmodifiedSince(lastModified);
Client client = new Client(Protocol.HTTP);
Response res = client.handle(get);

if(res.getStatus().equals(Status.REDIRECTION_NOT_MODIFIED)){
	//it means the representation has not been changed and server didn't send the representation
	//then get representation from Cache or File or Database
}else if(res.getStatus().equals(Status.SUCCESS_OK)){
	try {
		System.out.println(res.getEntity().getText());
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
}

 

我来解释一下这两段代码,正如刚开始解释条件GET中所说,如果客户端只请求一次而从此不在请求,那么永远不存在条件GET一说,因为第一次请求肯定不是条件GET,当第二次或者以后的请求,才可能满足条件GET的条件。所以,首先从客户端代码开始分析。

 

当然为了测试方便,上述代码的变更时间都是硬编码,实际环境中,应该是保存到数据库或者文件中.先说一下大概的流程:

 

客户端要发起请求,客户端上次从服务器接收表示的时间是2009-07-28,按照今天的时间来算,就是最后一次接收表示的时间是昨天。而客户端设置最后的变更时间为2009-07-28,实际上也是告诉服务器客户端的最后的表示的变更时间。

 

而服务器收到请求,并查看客户端是否附送了最后的变更时间,如果存在,则与当前服务器端表示的最后的变更时间做比较,如果在客户端附送的时间之后,服务器端的表示有变更,则重新发送新的表示和最新的变更时间,否则发送响应代码304,也就是Not Modified。

 

客户端如果收到响应是304,则知道服务器端的表示没有变更,所以,直接从缓存或者之前保存的文件或者数据库中取出之前的表示。而如果客户端接收新的表示,则根据架构的设计重新缓存或者保存表示以及最后变更时间到文件和数据库。

 

 上面的解释如果觉得有点糊涂,没关系,来个具体的例子,以Customer为例:

 

客户端请求的URI是:/customers/{customerId}, 返回客户端的表示的类型是XML,象如下这样:

<customer id="1" name="ajax">
	<address></address>
	<phone></phone>
	<fax></fax>
</customer>

 

name是根据Id得到,而客户端发送请求的目的是想知道id为1的customer的address、phone、fax是否有变化。而服务器端则会监控customer表中的这三个属性,如果任何一个有变化,则更新最后变更时间,这个最后变更时间应该有一个单独的数据库字段记录。所以,当客户端发送请求时,并附送客户端最后的接收表示时,从服务器端获取的变更时间。然后服务器会把数据库保存的时间跟客户端送来的时间做对比,来决定是否发送表示。

  

上面的解决方案貌似完美,但是即使服务器提供Last-Modified,也不是完全可靠,因为资源变化时间是无法百分之百精确记录。所以人们需要一种可靠的方式检查一个表示自从上次获取以后有没有发生变化。Etag响应报头就是用于此目的。Etag是一个无意义的字符串,它会随着对应的表示的变化而变化。

 

这也是HTTP/1.1中把Last-Modified作为weak validator的原因,关于什么是强校验器,什么是弱校验器,这里不作解释,感兴趣的可以查看HTTP/1.1的相关资料。

 

ok,基于上述,来看一下代码应该是什么样子,首先在客户端代码加入:

 

String customerToMD5 = Engine.getInstance().toMd5("1/ajax/shanghai/1388888888/12345678");
List<Tag> tags = new ArrayList<Tag>();
tags.add(new Tag(customerToMD5));
get.getConditions().setNoneMatch(tags);

 

 

上述代码的tag是硬编码产生的,仅仅为了测试。实际环境应该是存储在某个地方,发送的时候从存储的位置取出来即可。

 

当服务器端返回响应后,如果有新的表示返回,一般会有新的Tag,所以应该保存这个新的Tag,以便下次请求时发送:

 

//Cache the last modified date or save it into File or Database
System.out.println(res.getEntity().getModificationDate());
//Cache the tag or save the tag into File or Database
System.out.println(res.getEntity().getTag());

 

服务器端代码:

@Override
public void handleGet() {
	Date modifiedSince = getRequest().getConditions().getUnmodifiedSince();
	List<Tag> tags = getRequest().getConditions().getNoneMatch();
	
	SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	Date lastModifiedAtServer = null;
	try {
		lastModifiedAtServer = format.parse("2009-07-29 00:00:00");
	} catch (ParseException e) {
		e.printStackTrace();
	}
	String customerToMD5 = Engine.getInstance().toMd5("1/ajax/shanghai/1388888888/12345678");
	
	boolean noneMatch = tags.contains(new Tag(customerToMD5));
					
	if (modifiedSince == null
			|| modifiedSince.getTime() < lastModifiedAtServer.getTime() || noneMatch) {
		// get user data from database through user id
		User user = null;
		Representation representation = new StringRepresentation(
				getUserXml(user), MediaType.TEXT_PLAIN);
		representation.setMediaType(MediaType.TEXT_PLAIN);
		representation.setTag(new Tag(customerToMD5));
		representation.setModificationDate(lastModifiedAtServer);
		getResponse().setStatus(Status.SUCCESS_OK);
		getResponse().setEntity(representation);
	} else {
		getResponse().setStatus(Status.REDIRECTION_NOT_MODIFIED);
	}
}

 

 

如果一个服务同时提供了-Modified和Etag,那么客户端可以在随后的请求里同时提供If-Modified-Since和If-None-Match。服务器应当做双重检查,并且仅当二者满足时菜返回表示。

分享到:
评论

相关推荐

    Restlet实战(二十六)事务 (Transaction)

    在《Restlet实战(二十六)事务 (Transaction)》这篇博文中,作者可能会详细讲解如何在Restlet框架中实现上述事务处理策略。Restlet是一个开源的Java框架,专门用于构建RESTful应用程序。它提供了丰富的API和工具,...

    restlet实现最简单的restful webservice

    本文将深入探讨如何使用Restlet来实现一个最简单的RESTful Web服务。 首先,了解REST的基本概念是必要的。REST强调的是资源的概念,通过URI(Uniform Resource Identifier)来标识,使用HTTP协议中的方法(如GET、...

    RESTLET开发(三)

    ### RESTLET开发(三):基于Spring的REST服务 #### 一、基于Spring配置的Rest简单服务 在本文档中,我们将深入探讨如何利用RESTlet框架与Spring框架结合,构建高效的RESTful服务。Spring框架因其强大的功能和灵活...

    Restlet开发实例

    Restlet提供了JAX-RS兼容的组件,使得开发者可以轻松地在Restlet环境中使用JAX-RS注解,如`@Path`、`@GET`、`@POST`等,实现REST服务的快速开发。 接下来,我们进入"RESTLET开发实例(二)使用Component、Application...

    restlet

    REST是一种轻量级的架构风格,广泛应用于互联网应用的开发,它强调通过简单的HTTP方法(如GET、POST、PUT、DELETE)来操作资源,以实现高效、可扩展的网络通信。 RESTlet框架提供了客户端和服务器端的组件,使得...

    org.restlet-2.3.0.jar 最新版本

    在Java开发领域,REST(Representational State Transfer)架构风格已经成为构建Web服务的主流选择,而org.restlet-2.3.0.jar则是实现RESTful API的重要库。此版本作为最新的迭代,具有卓越的兼容性和稳定性,旨在为...

    RESTLET开发

    实现一个具体的资源类`StudentResource`,该类需要继承自`javax.ws.rs.core.Resource`,并在其中定义RESTful方法(如`GET`、`POST`等)来处理客户端请求。 #### 四、总结 通过上述步骤,我们可以构建出一个基本的...

    restlet restful

    总的来说,"restlet restful"项目是一个基于RESTlet框架的RESTful Web服务实现,提供了便捷的客户端和服务端通信方式,便于开发和测试。通过"RestApplication"类,我们可以创建和管理REST资源,实现对HTTP请求的处理...

    restlet处理各种请求方式参考示例

    在Restlet中,你可以通过覆盖`handle`方法或者重写`ServerResource`类的`get`方法来处理GET请求。例如: ```java public class MyResource extends ServerResource { @Get public Representation get() { // ...

    restlet项目

    Restlet项目是一个开源框架,专门用于构建RESTful(Representational State Transfer)Web服务。REST是一种软件架构风格,它强调简洁、可扩展性和无状态性,是Web服务设计的主流方式。Restlet框架提供了全面的工具集...

    Restlet2 + Spring3 注解方式配置

    在本文中,我们将深入探讨如何在Spring 3框架中集成Restlet 2,利用注解方式进行配置。Restlet是一个轻量级的Java RESTful Web服务开发库,而Spring则是一个广泛使用的全面的企业级应用框架。结合两者,我们可以创建...

    Restlet in action 英文 完整版

    书中不仅介绍了Restlet框架的基本概念和技术细节,还通过丰富的示例代码展示了如何设计、实现和部署RESTful Web服务。 #### 二、Restlet框架简介 **Restlet** 是一个灵活且强大的Java框架,用于构建RESTful Web...

    restlet2.1学习笔记项目代码

    在Restlet中,这些表示通常通过`org.restlet.data.MediaType`定义,并由`Representation`接口的实现来处理。 4. **路由(Route)**:Restlet使用`org.restlet.routing.Router`来映射不同的URI路径到相应的资源。这...

    Restlet Client 插件安装包

    6. **自动化测试**: 虽然`Restlet Client`主要作为手动测试工具,但通过编写脚本,也可以实现一定程度的自动化测试。 7. **安全性**: 支持OAuth 2.0和其他身份验证机制,保障API测试的安全性。 ### 使用场景 - **...

    Restlet学习的三篇文章

    Restlet框架提供了更底层的REST服务实现,它不仅支持JAX-RS规范,还提供了自己的API。Restlet组件模型包括客户端和服务器端的部分,如代表资源的`Representation`、处理请求的`Resource`、管理网络连接的`Connector...

    Restlet开发的Basic认证

    Restlet是一个轻量级的Java Web服务开发框架,它提供了构建RESTful(Representational State Transfer)应用程序的工具和API。REST是一种架构风格,强调简洁、无状态和可缓存的网络交互,常用于构建高性能、高可用性...

    基于Spring的Restlet实例

    因此,我们需要借助其他库如Spring Boot或Springfox等,或者像本例中的Restlet,来实现REST服务。 3. **Restlet库**:Restlet提供了用于构建RESTful服务的API,包括服务器端和客户端组件。它支持HTTP、HTTPS、...

Global site tag (gtag.js) - Google Analytics