使用Spring MVC建立 REST API

 

1.REST的基础知识

当谈论REST时,有一种常见的错误就是将其视为“基于URL的Web服务”——将REST做为另外一种类型的远程过程调用(remote procedurecall,RPC)机制,就像SOAP同样,只不过是经过简单的HTTP URL来触发,而不是使用SOAP大量的XML命名空间。刚好相反,REST与RPC几乎没有任何关系。RPC是面向服务的,并关注于行为和动做;而REST是面向资源的,强调描述应用程序的事物和名词。为了理解REST是什么,咱们将它的首字母缩写拆分为不一样的构成部分:html

 

表述性(Representational):REST资源实际上能够用各类形式来进行表述,包括XML、JSON(JavaScript Object Notation)甚至HTML——最适合资源使用者的任意形式;编程

状态(State):当使用REST的时候,咱们更关注资源的状态而不是对资源采起的行为;json

转移(Transfer):REST涉及到转移资源数据,它以某种表述性形式从一个应用转移到另外一个应用。更简洁地讲,REST就是将资源的状态以最适合客户端或服务端的形式从服务器端转移到客户端(或者反过来)。在REST中,资源经过URL进行识别和定位。至于RESTful URL的结构并无严格的规则,可是URL应该可以识别资源,而不是简单的发一条命令到服务器上。再次强调,关注的核心是事物,而不是行为浏览器

REST中会有行为,它们是经过HTTP方法来定义的。具体来说,也就是GET、POST、PUT、DELETE、PATCH以及其余的HTTP方法构成了服务器

REST中的动做。这些HTTP方法一般会匹配为以下的CRUD动做:app

Create:POSTRead:GETide

Update:PUT或PATCH学习

Delete:DELETEspa

 

 

尽管一般来说,HTTP方法会映射为CRUD动做,但这并非严格的限制。有时候,PUT能够用来建立新资源,POST能够用来更新资源。实际上,POST请求非幂等性(non-idempotent)的特色使其成为一个很是灵活的方法,对于没法适应其余HTTP方法语义的操做,它都能够胜任。插件

 

2.Spring是如何支持REST的

当前的4.0版本中,Spring支持如下方式来建立REST资源:

控制器能够处理全部的HTTP方法,包含四个主要的REST方法:GET、PUT、DELETE以及POST。Spring 3.2及以上版本还支持PATCH方法;

借助@PathVariable注解,控制器可以处理参数化的URL(将变量输入做为URL的一部分);借助Spring的视图和视图解析器,资源可以以多种方式进行表述,包括将模型数据渲染为XML、JSON、Atom以及RSS的View实现;可使用ContentNegotiatingViewResolver来选择最适合客户端的表述;借助@ResponseBody注解和各类HttpMethodConverter实现,可以替换基于视图的渲染方式;相似地,@RequestBody注解以及HttpMethodConverter实现能够将传入的HTTP数据转化为传入控制器处理方法的Java对象;借助RestTemplate,Spring应用可以方便地使用REST资源。

 

3.建立第一个REST端点

首先,咱们会在名为SpittleApiController的新控制器中创建第一个REST端点。以下的程序清单展示了这个新REST控制器起始的样子,它会提供Spittle资源。这是一个很简单的开始,可是在本章中,随着不断学习Spring REST编程模型的细节,咱们将会不断构建这个控制器

 

  对于非人类用户的使用者,好比其余的应用或调用REST端点的代码,资源表述的首选应该是XML和JSON。借助Spring同时支持这两种方案很是简单,因此没有必要作一个非此即彼的选择。按照个人意见,我推荐至少要支持JSON。JSON使用起来至少会像XML同样简单(不少人会说JSON会更加简单),而且若是客户端是JavaScript(最近一段时间以来,这种作法愈来愈常见)的话,JSON更是会成为优胜者,由于在JavaScript中使用JSON数据根本就不须要编排和解排(marshaling/demarshaling)

 

 

须要了解的是控制器自己一般并不关心资源如何表述。控制器以Java对象的方式来处理资源。控制器完成了它的工做以后,资源才会被转化成最适合客户端的形式。Spring提供了两种方法将资源的Java表述形式转换为发送给客户端的表述形式:

内容协商(Content negotiation):选择一个视图,它可以将模型
渲染为呈现给客户端的表述形式;
消息转换器(Message conversion):经过一个消息转换器将控
制器所返回的对象转换为呈现给客户端的表述形式。

 

协商资源表述

 

你能够回忆一下在第5章中(以及图5.1所示),当控制器的处理方法完成时,一般会返回一个逻辑视图名。若是方法不直接返回逻辑视图名(例如方法返回void),那么逻辑视图名会根据请求的URL判断得出。DispatcherServlet接下来会将视图的名字传递给一个视图解析器,要求它来帮助肯定应该用哪一个视图来渲染请求结果。在面向人类访问的Web应用程序中,选择的视图一般来说都会渲染为HTML。视图解析方案是个简单的一维活动。若是根据视图名匹配上了视图,那这就是咱们要用的视图了。当要将视图名解析为可以产生资源表述的视图时,咱们就有另一个维度须要考虑了。视图不只要匹配视图名,并且所选择的视图要适合客户端。若是客户端想要JSON,那么渲染HTML的视图就不行了——尽管视图名可能匹配。

Spring的ContentNegotiatingViewResolver是一个特殊的视图解析器,它考虑到了客户端所须要的内容类型。按照其最简单的形式ContentNegotiatingViewResolver能够按照下述形式进行配置:

 

 在这个简单的bean声明背后会涉及到不少事情。要理解ContentNegotiating-ViewResolver是如何工做的,这涉及内容协商的两个步骤

 

1.肯定请求的媒体类型;
2.找到适合请求媒体类型的最佳视图。

 

 

肯定请求的媒体类型

  在内容协商两步骤中,第一步是肯定客户端想要什么类型的内容表述。表面上看,这彷佛是一个很简单的事情。难道请求的Accept头部信息不是已经很清楚地代表要发送什么样的表述给客户端吗?遗憾的是,Accept头部信息并不老是可靠的。若是客户端是Web浏览器,那并不能保证客户端须要的类型就是浏览器在Accept头部所发送的值。Web浏览器通常只接受对人类用户友好的内容类型(如text/html),因此没有办法(除了面向开发人员的浏览器插件)指定不一样的内容类型。

  ContentNegotiatingViewResolver将会考虑到Accept头部信息并使用它所请求的媒体类型,可是它会首先查看URL的文件扩展名。若是URL在结尾处有文件扩展名的话,ContentNegotiatingViewResolver将会基于该扩展名肯定所需的类型。若是扩展名是“.json”的话,那么所需的内容类型必须是“application/json”。若是扩展名是“.xml”,那么客户端请求的就是“application/xml”。固然,“.html”扩展名代表客户端所需的资源表述为HTML(text/html)。若是根据文件扩展名不能获得任何媒体类型的话,那就会考虑请求中的Accept头部信息。在这种状况下,Accept头部信息中的值就代表了客户端想要的MIME类型,没有必要再去查找了。最后,若是没有Accept头部信息,而且扩展名也没法提供帮助的话,ContentNegotiatingViewResolver将会使用“/”做为默认的内容类型,这就意味着客户端必需要接收服务器发送的任何形式的表述。一旦内容类型肯定以后,ContentNegotiatingViewResolver就该将逻辑视图名解析为渲染模型的View。

  与Spring的其余视图解析器不一样,ContentNegotiatingViewResolver自己不会解析视图。而是委托给其余的视图解析器,让它们来解析视图。ContentNegotiatingViewResolver要求其余的视图解析器将逻辑视图名解析为视图。解析获得的每一个视图都会放到一个列表中。这个列表装配完成后,ContentNegotiatingViewResolver会循环客户端请求的全部媒体类型,在候选的视图中查找可以产生对应内容类型的视图。第一个匹配的视图会用来渲染模型。

 

 

影响媒体类型的选择

在上述的选择过程当中,咱们阐述了肯定所请求媒体类型的默认策略。可是经过为其设置一个ContentNegotiationManager,咱们可以改变它的行为。借助Content-NegotiationManager咱们所能作到的事情以下所示:

 

1.指定默认的内容类型,若是根据请求没法获得内容类型的话,将会使用默认值;

2.经过请求参数指定内容类型;

3.忽视请求的Accept头部信息;
4.将请求的扩展名映射为特定的媒体类型;
5.
将JAF(Java Activation Framework)做为根据扩展名查找媒体类

型的备用方案

 

有三种配置ContentNegotiationManager的方法:

直接声明一个ContentNegotiationManager类型的bean;

经过ContentNegotiationManagerFactoryBean间接建立bean;

重载WebMvcConfigurerAdapter的configureContentNegotiation()方法。

直接建立ContentNegotiationManager有一些复杂,除非有充分的缘由,不然咱们不会愿意这样作。后两种方案可以让建立ContentNegotiationManager更加简单。

 

通常而言,若是咱们使用XML配置ContentNegotiationManager的话,那最有用的将会是ContentNegotiationManagerFactoryBean。例如,咱们可能但愿在XML中配置ContentNegotiationManager使

用“application/json”做为默认的内容类型:

由于ContentNegotiationManagerFactoryBean是FactoryBean的实现,因此它会建立一个ContentNegotiationManagerbean。这个ContentNegotiationManager可以注入到ContentNegotiatingViewResolver的contentNegotiationManager属性中。

 

 若是使用Java配置的话,得到ContentNegotiationManager的最简便方法就是扩展WebMvcConfigurerAdapter并重载configureContentNegotiation()方法。在建立Spring MVC应用的时候,咱们极可能已经扩展了WebMvcConfigurerAdapter。例如,在Spittr应用中,咱们已经有了WebMvcConfigurerAdapter的扩展类,名为WebConfig,因此须要作的就是重载configureContentNegotiation()方法。如下就是configureContentNegotiation()的一个实现,它设置了默认的内容类型:

 

咱们能够看到,configureContentNegotiation()方法给定了一个Content-NegotiationConfigurer对象。ContentNegotiationConfigurer中的一些方法对应于ContentNegotiationManager的Setter方法,这样咱们就能在ContentNegotiation-Manager建立时,设置任意内容协商相关的属性。在本例中,咱们调用defaultContentType()方法将默认的内容类型设置为“application/json”。

配置ContentNegotiationManager有不少的细节,在这里没法对它们进行一一介绍。以下的程序清单是一个很是简单的配置样例,当我使用ContentNegotiating-ViewResolver的时候,一般会采用这种用法:它默认会使用HTML视图,可是对特定的视图名称将会渲染为JSON输出。

 

  除了程序清单16.2中的内容之外,还应该有一个可以处理HTML的视图解析器(如InternalResourceViewResolver或TilesViewResolver)。在大多数场景下,ContentNegotiatingViewResolver会假设客户端须要HTML,如ContentNegotiationManager配置所示。可是,若是客户端指定了它想要JSON(经过在请求路径上使用“.json”扩展名或Accept头部信息)的话,那么ContentNegotiatingViewResolver将会查找可以处理JSON视图的视图解析器。若是逻辑视图的名称为“spittles”,那么咱们所配置的BeanNameViewResolver将会解析spittles()方法中所声明的View。这是由于bean名称匹配逻辑视图的名称。若是没有匹配的View的话ContentNegotiatingViewResolver将会采用默认的行为,将其输出为HTML。ContentNegotiatingViewResolver一旦可以肯定客户端想要什么样的媒体类型,接下来就是查找渲染这种内容的视图

 

5.使用HTTP信息转换器

消息转换(message conversion)提供了一种更为直接的方式,它可以将控制器产生的数据转换为服务于客户端的表述形式。当使用消息转换功能时,DispatcherServlet再也不须要那么麻烦地将模型数据传送到视图中。实际上,这里根本就没有模型,也没有视图,只有控制器产生的数据,以及消息转换器(message converter)转换数据之后所产生的资源表述。

 

  例如,假设客户端经过请求的Accept头信息代表它能接受“application/json”,而且Jackson JSON在类路径下,那么处理方法返回的对象将交给MappingJacksonHttp-MessageConverter,并由它转换为返回客户端的JSON表述形式。另外一方面,若是请求的头信息代表客户端想要“text/xml”格式,那么Jaxb2RootElementHttpMessage-Converter将会为客户端产生XML响应。

  注意,表16.2中的HTTP信息转换器除了其中的五个之外都是自动注册的,因此要使用它们的话,不须要Spring配置。可是为了支持它们,你须要添加一些库到应用程序的类路径下。例如,若是你想使用MappingJacksonHttpMessageConverter来实现JSON消息和Java对象的互相转换,那么须要将Jackson JSON Processor库添加到类路径中。相似地,若是你想使用Jaxb2RootElementHttpMessageConverter来实现XML消息和Java对象的互相转换,那么须要JAXB库。若是信息是Atom或RSS格式的话,那么Atom-FeedHttpMessageConverter和RssChannelHttpMessageConverter会须要Rome库

 

你可能已经猜到了,为了支持消息转换,咱们须要对Spring MVC的编程模型进行一些小调整。在响应体中返回资源状态正常状况下,当处理方法返回Java对象(除String外或View的实现以外)时,这个对象会放在模型中并在视图中渲染使用。可是,若是使用了消息转换功能的话,咱们须要告诉Spring跳过正常的模型/视图流程,并使用消息转换器。有很多方式都能作到这一点,可是最简单的方法是为控制器方法添加@ResponseBody注解。

 

 

 

在请求体中接收资源状态
到目前为止,咱们只关注了REST端点如何为客户端提供资源。可是
REST并非只读的,REST API也能够接受来自客户端的资源表述。
若是要让控制器将客户端发送的JSON和XML转换为它所使用的Java
对象,那是很是不方便的。在处理逻辑离开控制器的时候,Spring的
消息转换器可以将对象转换为表述——它们能不能在表述传入的时候
完成相同的任务呢?
@ResponseBody可以告诉Spring在把数据发送给客户端的时候,要
使用某一个消息器,与之相似,@RequestBody也能告诉Spring查找
一个消息转换器,未来自客户端的资源表述转换为对象。例如,假设
咱们须要一种方式将客户端提交的新Spittle保存起来。咱们能够
按照以下的方式编写控制器方法来处理这种请求

 

 

 

@ResponseBody注解会告知Spring,咱们要将返回的对象做为资源发送给客户端,并将其转换为客户端可接受的表述形式。更具体地讲,DispatcherServlet将会考虑到请求中Accept头部信息,并查找可以为客户端提供所需表述形式的消息转换器。举例来说,假设客户端的Accept头部信息代表它接受“application/json”,而且Jackson JSON库位于应用的类路径下,那么将会选择MappingJacksonHttpMessage-Converter或MappingJackson2HttpMessageConverter(这取决于类路径下是哪一个版本的Jackson)。消息转换器会将控制器返回的Spittle列表转换为JSON文档,并将其写入到响应体中。响应大体会以下所示

 

在请求体中接收资源状态到目前为止,咱们只关注了REST端点如何为客户端提供资源。可是REST并非只读的,REST API也能够接受来自客户端的资源表述。若是要让控制器将客户端发送的JSON和XML转换为它所使用的Java对象,那是很是不方便的。在处理逻辑离开控制器的时候,Spring的消息转换器可以将对象转换为表述——它们能不能在表述传入的时候完成相同的任务呢?

@ResponseBody可以告诉Spring在把数据发送给客户端的时候,要使用某一个消息器,与之相似,@RequestBody也能告诉Spring查找一个消息转换器,未来自客户端的资源表述转换为对象。例如,假设咱们须要一种方式将客户端提交的新Spittle保存起来。咱们能够按照以下的方式编写控制器方法来处理这种请求

@RequestBody,因此Spring将会查看请求中的Content-Type头部信息,并查找可以将请求体转换为Spittle的消息转换器。例如,若是客户端发送的Spittle数据是JSON表述形式,那么Content-Type头部信息可能就会是“application/json”。在这种状况下,DispatcherServlet会查找可以将JSON转换为Java对象的消息转换器。若是Jackson 2库在类路径中,那么MappingJackson2HttpMessageConverter将会担此重任,将JSON表述转换为Spittle,而后传递到saveSpittle()方法中。这个方法还使用了@ResponseBody注解,所以方法返回的Spittle对象将会转换为某种资源表述,发送给客户端。注意,@RequestMapping有一个consumes属性,咱们将其设置为“application/ json”。consumes属性的工做方式相似于produces,不过它会关注请求的Content-Type头部信息。它会告诉Spring这个方法只会处理对“/spittles”的POST请求,而且要求请求的Content-Type头部信息为“application/json”。若是无法知足这些条件的话,会由其余方法(若是存在合适的方法的话)来处理请求。

 

为控制器默认设置消息转换

 

当处理请求时,@ResponseBody和@RequestBody是启用消息转换的一种简洁和强大方式。可是,若是你所编写的控制器有多个方法,而且每一个方法都须要信息转换功能的话,那么这些注解就会带来必定程度的重复性。Spring 4.0引入了@RestController注解,可以在这个方面给咱们提供帮助。若是在控制器类上使用@RestController来代替@Controller的话,Spring将会为该控制器的全部处理方法应用消息转换功能。咱们没必要为每一个方法都添加@ResponseBody了。咱们所定义的SpittleController可能就会以下所示:

 咱们看到了如何使用Spring MVC编程模型将RESTful资源发布到响应体之中。可是响应除了负载之外还会有其余的内容。头部信息和状态码也可以为客户端提供响应的有用信息。接下来,咱们

看一下在提供资源的时候,如何填充头部信息和设置状态码

相关文章
相关标签/搜索