第11章 为Spring添加REST功能

来源:互联网 发布:mirror.centos.org 编辑:程序博客网 时间:2024/06/05 16:56

第11章 为Spring添加REST功能

数据为王。

为信息为中心的表述性状态转移(Representational State Trannfer,REST)已成为替换传统SOAP Web服务的流行方案。

11.1 了解REST

11.1.1 REST的基本原理

REST与RPC几乎没有任何关系。RPC是面向服务的,并关注与行为和动作;而REST是面向资源的,强调描述应用程序的事务和名词。

*表述性(Representational)——REST资源实际上可以用各种形式来进行表述,包括XML,JSON,甚至HTML——最适合资源使用者的任意形式。

*状态(State)——当使用REST的时候,我们更关注资源的状态而不是对资源采取的行为。

*转移(Transfer)——REST涉及转移资源数据,它以某一种表述性形式从一个应用转移到了一个应用。

11.1.2  Spring是如何支持REST的


11.2 编写面向资源的控制器

不符合RESTful资源的控制器就是RESTless。

11.2.1 剖析RESTless的控制器

<span style="font-size:18px;">@Controller@RequestMapping("/displaySpittle.htm") //<co id="co_restlessRequestMapping"/> public class DisplaySpittleController {  private final SpitterService spitterService;  @Inject  public DisplaySpittleController(SpitterService spitterService) {    this.spitterService = spitterService;  }    @RequestMapping(method=RequestMethod.GET)  public String showSpittle(@RequestParam("id") long id, Model model) {    model.addAttribute(spitterService.getSpittleById(id));    return "spittles/view";  }}</span>
DisplaySpittleController的编写方式并没有严重的错误。但是,它并不是一个RESTful的控制器。它是面向行为的并关注于一个特殊的用例:以HTML的形式展现一个Spittle对象的详细信息。

11.2.2 处理RESTful URL

URL是统一资源定位符的缩写,本意是用于定为资源的。此外,所有的URL同时也都是URI或统一资源标识符。



在URL中嵌入参数

<span style="font-size:18px;">@Controller@RequestMapping("/spittles") //<co id="co_spittlesRequestMapping"/> public class SpittleController {  private SpitterService spitterService;    @Inject  public SpittleController(SpitterService spitterService) {    this.spitterService = spitterService;  }    @RequestMapping(value="/{id}",                   method=RequestMethod.GET) //<co id="co_requestMapping_getSpittleMethod"/>  public String getSpittle(@PathVariable("id") long id,          Model model) {    model.addAttribute(spitterService.getSpittleById(id));    return "spittles/view";  }}</span>
"spittles/{id}"这种URL格式的GET请求,{id}是一个占位符,通过它会将变量数据传递给方法。它对应了方法参数id的@PathVariable注解。

11.2.3 执行REST动作

GET请求将资源的状态从服务器转移到客户端 ,而PUT将资源的状态从客户端转移到服务器上。

使用PUT更新资源

<span style="font-size:18px;">  @RequestMapping(value="/{id}", method=RequestMethod.PUT)  @ResponseStatus(HttpStatus.NO_CONTENT)  public void putSpittle(@PathVariable("id") long id,          @Valid Spittle spittle) {    spitterService.saveSpittle(spittle);  }</span>
<p><span style="font-size:18px;">@ResponseStatus注解定义了HTTP状态,这个状态要设置在发往客户端的响应中。HttpStatus.NO_CONTENT说明响应状态要设置为HTTP状态码204。这个状态码意味着请求被成功处理了,但是在响应体重不包括任何返回信息。</span></p>
处理DELETE请求

<span style="font-size:18px;"> @RequestMapping(value="/{id}", method=RequestMethod.DELETE)  @ResponseStatus(HttpStatus.NO_CONTENT)  public void deleteSpittle(@PathVariable("id") long id) {    spitterService.deleteSpittle(id);  }</span>
使用POST创建资源

<span style="font-size:18px;">  @RequestMapping(method=RequestMethod.POST) //<co id="co_handlePOST"/>   @ResponseStatus(HttpStatus.CREATED) // <co id="co_createdResponse" />  public @ResponseBody Spittle createSpittle(@Valid Spittle spittle,                      BindingResult result, HttpServletResponse response)                      throws BindException {    if(result.hasErrors()) {      throw new BindException(result);    }        spitterService.saveSpittle(spittle);        response.setHeader("Location", "/spittles/" + spittle.getId()); //<co id="co_setLocationHeader"/>    return spittle; //<co id="co_returnSpittle"/>  }</span>

11.3 表述资源

表述是REST中很重要的一个方面。它是关于客户端与服务器端针对某一资源是如何通信的。任何给定的资源都几乎可以用任意的形式来进行表述。

控制器以Java对象的方式来处理资源。控制器完成了它的工作之后,资源才会被转化成最适合客户端的形式。

Spring提供了两种方法将资源的Java表述形式转化为发送给客户端的表述形式:

*基于试图渲染进行协商

*HTTP消息转化器

11.3.1 协商资源表述

如果方法不直接返回逻辑试图名(例如方法返回void),那么逻辑试图名会来源于请求的URL。

Spring的ContentNegotiatingViewResolver是一个特殊的视图解析器,它考虑到了客户端所需要的内容类型。

<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">   <property name="mediaTypes">     <map>       <entry key="json" value="application/json" />      <entry key="xml" value="text/xml" />      <entry key="htm" value="text/html" />     </map>   </property>   <property name="defaultContentType" value="text/html" /> </bean>
要理解ContentNegotiatingViewResolver是如何工作的,要涉及内容协商的两个步骤:

1 确定请求的媒体类型

2 找到适合请求媒体类型的最佳视图

确定请求的媒体类型
影响如何选择媒体类型

查找视图

11.3.2 使用HTTP信息转化器

在响应体中返回资源状态

@RequestMapping(value="/(username)",method=RequestMethod.GET,headers = {*Accept = text/xml, application/json*})

public @ResponseBody Spitter getSpitter(@PathVariable String username){

return spitterService.getSpitter(username);

}

@ResponseBody注解会告知Spring,将返回的对象作为资源发送给客户端,并将其转换为客户端可接受的表述形式。



在请求中接收资源状态

@ResponseBody注解能够将发送给客户端的数据进行转换一样,@RequestBody也能够对客户端发过来的对象做相同的事情。

@RequestMapping(value = */{username}*, method = RequestMethod.PUT, headers = *Content-Type = application/json*}

@ResponseStatus(HttpStatus.NO_CONTENT)

public void updateSpitter(@PathVariable String username,@RequestBody Spitter spitter) {
spitterService.saveSpitter(spitter);

}

JSON信息转换为Spitter对象,条件:

*请求的Content-Type头信息必须是application/json;

*Jackson JSON库必须包含在应用程序的类路径下。

11.4 编写REST客户端

public Spittle[] retrieveSpittlesForSpitter(String username) {    try {      HttpClient httpClient = new DefaultHttpClient();//<co id="co_createHttpClient"/>             String spittleUrl = "http://localhost:8080/Spitter/spitters/" +                           username + "/spittles";//<co id="co_constructUrl"/>            HttpGet getRequest = new HttpGet(spittleUrl);//<co id="co_createRequest"/>            getRequest.setHeader(              new BasicHeader("Accept", "application/json"));            HttpResponse response = httpClient.execute(getRequest);//<co id="co_executeRequest"/>            HttpEntity entity = response.getEntity();//<co id="co_parseResult"/>      ObjectMapper mapper = new ObjectMapper();      return mapper.readValue(entity.getContent(), Spittle[].class);    } catch (IOException e) {      throw new SpitterClientException("Unable to retrieve Spittles", e);    }  }
在资源使用上有如此之多的样板代码,你可能会觉得最好的方式是封装通过代码并参数化可变的地方。这正是Spring的RestTemplate所做的事情。








0 0