Create a REST API with Spring MVC - 2

1. Provide content other than resources

@ResponseBody provides a useful way to convert the Java object returned by the controller into a resource representation sent to the client. In fact, sending the resource representation to the client is only part of the whole process. A good REST API can not only pass resources between client and server, it can also provide additional metadata to the client to help the client understand the resource or what happened in the request.

 

Send error message to client

 

What do you think will happen when the findOne() method returns null if the ID property of a Spittle object cannot be found to match with the given ID? The result is that the spittleById() method will return null, the response body will be empty, and no useful data will be returned to the client. Also, the default HTTP status code in the response is 200 (OK), which means everything is working fine

 

Spring provides several ways to handle such scenarios:

  1. Use the @ResponseStatus annotation to specify the status code;
  2. The controller method can return a ResponseEntity object, which can contain more response-related metadata;
  3. Exception handlers can handle error scenarios so that handler methods can focus on normal situations.

 

 

 

Use ResponseEntity

As an alternative to @ResponseBody, controller methods can return a ResponseEntity object. ResponseEntity can contain response-related metadata (such as header information and status codes) and objects to be converted into resource representations. Because ResponseEntity allows us to specify the status code of the response, we can return an HTTP 404 error when the Spittle cannot be found. Here's the new version of spittleById(), which returns a ResponseEntity:

 

 

 Let's refactor the code to use error handlers. First, define an error handler that corresponds to SpittleNotFound-Exception

 

 

The @ExceptionHandler annotation can be used in controller methods to handle specific exceptions. Here, it indicates that if a SpittleNotFoundException is thrown in any of the controller's handler methods, the spittleNotFound() method will be called to handle the exception. As for SpittleNotFoundException, it's a very simple exception class:

 

 

 

 Set headers in the response

 

After saveSpittle() finishes processing the request, the server returns the client with the Spittle representation and HTTP status code 200 (OK) in the response body. There's no big problem here, but it's not entirely accurate yet. Of course, assuming the resource was successfully created while processing the request, the status can be considered OK. But we don't just need to say "OK". We create new content and the HTTP status code tells the client about this. However, HTTP 201 can not only indicate that the request completed successfully, but can also describe the creation of a new resource. If we want to communicate completely and accurately with the client, shouldn't the response be 201 (Created), not just 200 (OK)? Based on what we've learned so far, this problem is easy to solve. All we need to do is to annotate the saveSpittle() method with @ResponseStatus as follows:

 

But that's only part of the problem. The client knows that the newly created resource is. Do you think the client will be interested in where the newly created resource is? After all, this is a newly created resource, and there will be a new URL associated with it. Can the client just guess what the URL of the newly created resource is? Can we tell it to the client somehow? When creating a new resource, it is a good idea to put the URL of the resource in the Location header of the response and return it to the client. So, we need a way to populate the response headers, and that's where our old friend ResponseEntity can help. The following program listing shows a new version of saveSpittle() that returns a ResponseEntity to tell the client about the newly created resource

 

 

我们其实没有必要手动构建URL,Spring提供了UriComponentsBuilder,可以给我们一些帮助。它是一个构建类,通过逐步指定URL中的各种组成部分(如host、端口、路径以及查询),我们能够使用它来构建UriComponents实例。借助UriComponentsBuilder所构建的UriComponents对象,我们就能获得适合设置给Location头部信息的URI。为了使用UriComponentsBuilder,我们需要做的就是在处理器方法中将其作为一个参数,如下面的程序清单所示。

 

2.编写REST客户端

 

了解RestTemplate的操作

RestTemplate定义了36个与REST资源交互的方法,其中的大多数都对应于HTTP的方法。但是,在本章中我没有足够的篇幅涵盖所有的36个方法。其实,这里面只有11个独立的方法,其中有十个有三种重载形式,而第十一个则重载了六次,这样一共形成了36个方法。表16.2描述了RestTemplate所提供的11个独立方法。除了TRACE以外,RestTemplate涵盖了所有的HTTP动作。除此之外,execute()和exchange()提供了较低层次的通用方法来使用任意的HTTP方法

 

表16.2中的大多数操作都以三种方法的形式进行了重载:

  1. 一个使用java.net.URI作为URL格式,不支持参数化URL;
  2. 一个使用String作为URL格式,并使用Map指明URL参数;
  3. 一个使用String作为URL格式,并使用可变参数列表指明URL参数。

 

明确了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。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325669766&siteId=291194637