@ResponseBody提供了一种颇有用的方式,可以将控制器返回的Java对象转换为发送到客户端的资源表述。实际上,将资源表述发送给客户端只是整个过程的一部分。一个好的REST API不只可以在客户端和服务器之间传递资源,它还可以给客户端提供额外的元数据,帮助客户端理解资源或者在请求中出现了什么状况。java
发送错误信息到客户端浏览器
若是根据给定的ID,没法找到某个Spittle对象的ID属性可以与之匹配,findOne()方法返回null的时候,你以为会发生什么呢?结果就是spittleById()方法会返回null,响应体为空,不会返回任何有用的数据给客户端。同时,响应中默认的HTTP状态码是200(OK),表示全部的事情运行正常服务器
Spring提供了多种方式来处理这样的场景:架构
使用ResponseEntity性能
做为@ResponseBody的替代方案,控制器方法能够返回一个ResponseEntity对象。ResponseEntity中能够包含响应相关的元数据(如头部信息和状态码)以及要转换成资源表述的对象。由于ResponseEntity容许咱们指定响应的状态码,因此当没法找到Spittle的时候,咱们能够返回HTTP 404错误。以下是新版本的spittleById(),它会返回ResponseEntity:fetch
咱们重构一下代码来使用错误处理器。首先,定义可以对应SpittleNotFound-Exception的错误处理器ui
@ExceptionHandler注解可以用到控制器方法中,用来处理特定的异常。这里,它代表若是在控制器的任意处理方法中抛出SpittleNotFoundException异常,就会调用spittleNotFound()方法来处理异常。至于SpittleNotFoundException,它是一个很简单异常类:spa
在响应中设置头部信息.net
在saveSpittle()处理完请求以后,服务器在响应体中包含了Spittle的表述以及HTTP状态码200(OK),将其返回给客户端。这里没有什么大问题,可是还不是彻底准确。固然,假设处理请求的过程当中成功建立了资源,状态能够视为OK。可是,咱们不只仅须要说“OK”。咱们建立了新的内容,HTTP状态码也将这种状况告诉给了客户端。不过,HTTP 201不只可以代表请求成功完成,并且还能描述建立了新资源。若是咱们但愿完整准确地与客户端交流,那么响应是否是应该为201(Created),而不只仅是200(OK)呢?根据咱们目前所学到的知识,这个问题解决起来很容易。咱们须要作的就是为saveSpittle()方法添加@ResponseStatus注解,以下所示:设计
但这只是问题的一部分。客户端知道新建立了资源,你以为客户端会不会感兴趣新建立的资源在哪里呢?毕竟,这是一个新建立的资源,会有一个新的URL与之关联。难道客户端只能猜想新建立资源的URL是什么吗?咱们能不能以某种方式将其告诉客户端?当建立新资源的时候,将资源的URL放在响应的Location头部信息中,并返回给客户端是一种很好的方式。所以,咱们须要有一种方式来填充响应头部信息,此时咱们的老朋友ResponseEntity就能提供帮助了。以下的程序清单展示了一个新版本的saveSpittle(),它会返回ResponseEntity用来告诉客户端新建立的资源
咱们其实没有必要手动构建URL,Spring提供了UriComponentsBuilder,能够给咱们一些帮助。它是一个构建类,经过逐步指定URL中的各类组成部分(如host、端口、路径以及查询),咱们可以使用它来构建UriComponents实例。借助UriComponentsBuilder所构建的UriComponents对象,咱们就能得到适合设置给Location头部信息的URI。为了使用UriComponentsBuilder,咱们须要作的就是在处理器方法中将其做为一个参数,以下面的程序清单所示。
了解RestTemplate的操做
RestTemplate定义了36个与REST资源交互的方法,其中的大多数都对应于HTTP的方法。可是,在本章中我没有足够的篇幅涵盖全部的36个方法。其实,这里面只有11个独立的方法,其中有十个有三种重载形式,而第十一个则重载了六次,这样一共造成了36个方法。表16.2描述了RestTemplate所提供的11个独立方法。除了TRACE之外,RestTemplate涵盖了全部的HTTP动做。除此以外,execute()和exchange()提供了较低层次的通用方法来使用任意的HTTP方法
表16.2中的大多数操做都以三种方法的形式进行了重载:
明确了RestTemplate所提供的11个操做以及各个变种如何工做之后,你就能以本身的方式编写使用REST资源的客户端了。咱们经过对四个主要HTTP方法的支持(也就是GET、PUT、DELETE和POST)来研究RestTemplate的操做。咱们从GET方法的getForObject()和getForEntity()开始
GET资源
你可能意识到在表16.2中列出了两种执行GET请求的方法:getForObject()和getForEntity()。正如以前所描述的,每一个方法又有三种形式的重载
除了返回类型,getForEntity()方法就是getForObject()方法的镜像。实际上,它们的工做方式大同小异。它们都执行根据URL检索资源的GET请求。它们都将资源根据responseType参数匹配为必定的类型。惟一的区别在于getForObject()只返回所请求类型的对象,而getForEntity()方法会返回请求的对象以及响应相关的额外信息。让咱们首先看一下稍微简单的getForObject()方法。而后再看看如何使用getForEntity()方法来从GET响应中获取更多的信息
检索资源
getForObject()方法是检索资源的合适选择。咱们请求一个资源并按照所选择的Java类型接收该资源。做为getForObject()可以作什么的一个简单示例,让咱们看一
下fetchFacebookProfile()的另外一个实现:
咱们没有使用字符串链接来构建URL,而是利用了RestTemplate能够接受参数化URL这一功能。URL中的{id}占位符最终将会用方法的id参数来填充。getForObject()方法的最后一个参数是大小可变的参数列表,每一个参数都会按出现顺序插入到指定URL的占位符中。另一种替代方案是将id参数放到Map中,并以id做为key,而后将这个Map做为最后一个参数传递给getForObject():
这里没有任何形式的JSON解析和对象映射。在表面之下,getForObject()为咱们将响应体转换为对象。它实现这些须要依赖表16.1中所列的HTTP消息转换器,与带有@ResponseBody注解的Spring MVC处理方法所使用的同样。这个方法也没有任何异常处理。这不是由于getForObject()不能抛出异常,而是由于它抛出的异常都是非检查型的。若是在getForObject()中有错误,将抛出非检查型RestClientException异常(或者它的一些子类)。若是愿意的话,你能够捕获它——但编译器不会强制你捕获它
相似的方法
RESTful架构使用Web标准来集成应用程序,使得交互变得简单天然。系统中的资源采用URL进行标识,使用HTTP方法进行管理而且会以一种或多种适合客户端的方式来进行表述。在本章中,咱们看到了如何编写响应RESTful资源管理请求的SpringMVC控制器。借助参数化的URL模式并将控制器处理方法与特定的HTTP方法关联,控制器可以响应对资源的GET、POST、PUT以及DELETE请求。为了响应这些请求,Spring可以将资源背后的数据以最适合客户端的形式展示。对于基于视图的响应,ContentNegotiatingViewResolver可以在多个视图解析器产生的视图中选择出最适合客户端指望内容类型的那一个。或者,控制器的处理方法能够借助@ResponseBody注解彻底绕过视图解析,并使用信息转换器将返回值转换为客户端的响应。REST API为客户端暴露了应用的功能,它们暴露功能的方式恐怕最原始的API设计者作梦都想不到。REST API的客户端一般是移动应用或运行在Web浏览器中的JavaScript。可是,Spring应用也能够借助RestTemplate来使用这些API。