第一章 构建Restful开放API的两种常用方式
随着近几年微服务这种架构风格的火热,很多人都对Restful API有了一定的认识,Martin Folwer发表的论文中关于微服务中服务间的通讯需采用轻量级通信机制,这种轻量级的通信机制必须是能够跨语言、跨平台的,通常采用Http资源API作为微服务的通信机制。下面就会为大家带来关于如何构建Restful API提供两种常用方式,在介绍如何构建Restful API的常用方式之前,先跟大家介绍和学习一下关于Restful的相关知识,为后面打下一定的基础。
1.Restful是什么?
1.1.Restful的定义
Representational State Transfer,中文翻译过来是“表现层状态转化”,对于Restful的定义百度百科是这么描述的:“一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件”。
1.1.1.资源
面向资源是REST最明显的特征,”资源”是一种信息实体,所谓”资源”,就是网络上的一个实体,或者说是网络上的一个具体信息,它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的实在。
1.1.2.表现层
“资源”是一种信息实体,它可以有多种外在表现形式。我们把”资源”具体呈现出来的形式,叫做它的”表现层”。
比如,文本可以用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式;图片可以用JPG格式表现,也可以用PNG格式表现。
URI只代表资源的实体,不代表它的形式。严格地说,有些网址最后的”.html”后缀名是不必要的,因为这个后缀名表示格式,属于”表现层”范畴,而URI应该只代表”资源”的位置。它的具体表现形式,应该在HTTP请求的头信息中用Accept和Content-Type字段指定,这两个字段才是对”表现层”的描述。
1.1.3.状态转化
访问一个网站,就代表了客户端和服务器的一个互动过程。在这个过程中,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生”状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是”表现层状态转化”。
客户端用到的手段,只能是HTTP协议。具体来说,就是HTTP协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
1.1.4.总结
综合上面的解释,我们总结一下什么是RESTful架构:
(1)每一个URI代表一种资源;
(2)客户端和服务器之间,传递这种资源的某种表现层;
(3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现”表现层状态转化”。
1.2.Restful API
对Restful API的定义是:符合REST设计标准的API
1.3.Restful API最佳实践
(1)使用名词而不是动词
正例:
/employees 返回employees集合
/employees/711 返回指定的employee
反例:
/getAllEmployees
/createNewEmployee
(2)Get方法和查询参数不应该涉及状态改变
使用PUT, POST 和DELETE 方法 而不是 GET 方法来改变状态,不要使用GET 进行状态改变:
GET /users/711?activate
GET /users/711/activate
(3)使用复数名词
/foods代替/food
/users代替/user
(4)使用子资源来表述资源间的关系
GET /cars/711/drivers 查询711号car所有的driver列表
GET /cars/711/dirvers/4 查询711号car的4号driver
(5)使用Http头声明序列化格式
在客户端和服务端,双方都要知道通讯的格式,格式在HTTP-Header中指定
Content-Type 定义请求格式
Accept 定义了接收相应的格式列表
(6)使用HATEOAS约束
HATEOAS(Hypermedia as the engine of application state)是REST架构风格中最复杂的约束,也是构建成熟REST服务的核心。它的重要性在于打破了客户端与服务器之间严格的契约,使得客户端能够自适应。
{
"content": "dev-0.0.6",
"user": {
"name": "sxp",
"age": 12,
"_links": {
"item": {
"href": "http://localhost:8080/data-acquisition/greeting/sxp/sxp"
}
}
}
}
(7)提供过滤、排序、字段选择、分页
GET /cars?color=red 返回红色的cars
GET /cars?seats<=2 返回小于两座位的cars集合
(8)API版本化
使得API版本变得强制性,不要发布无版本的API,使用简单数字,避免小数点如2.5.
/blog/api/v1
(9)充分使用HTTP状态码来处理错误
HTTP状态码是用来表示网页服务器HTTP响应状态的3为数字码。在设计API处理错误时,应该充分使用HTTP状态码,而不是简单的抛出一个”500-服务器错误”。
所有的异常都应该有一个错误的payload作为映射,下面是一个例子:
{
"errors": [
{
"userMessage": "Sorry, the requested resource does not exist",
"internalMessage": "No car found in the database",
"code": 34,
"more info": "http://dev.mwaysolutions.com/blog/api/v1/errors/12345"
}
]
}
1.4.构建Restful API常用方式
由于Spring Boot能够快速构建项目,所以本书所有的项目都是基于Spring Boot开发的工程,如果读者对Spring Boot不太了解,请自行阅读相关资料学习。
首先新建一个Spring Boot项目,命名为:restful-project, 本书所有的项目使用Spring Boot 1.5.8.RELEASE版本构建。
1.4.1.Spring MVC构建Restful API
(1)在工程中引入web starter pom maven构建
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
(2)新建一个controller包,在该包中创建一个Controller类叫做GreetingController
@Controller
public class GreetingController {
/**
* The constant TEMPLATE.
*/
private static final String TEMPLATE = "Hello, %s!";
/**
* Greeting http entity.
*
* @param name the name
* @return the http entity
*/
@GetMapping("/api/greeting", produces = {"application/json"})
public HttpEntity<Greeting> greeting(
@RequestParam(value = "name") String name) {
Greeting greeting = new Greeting(String.format(TEMPLATE, name));
GreetUser greetUser = new GreetUser();
greetUser.setName("sxp");
greetUser.setAge(25);
greeting.setUser(greetUser);
return new ResponseEntity<>(greeting, HttpStatus.OK);
}
(3)这样一个Restful api就构建完成了。
备注:大家看到上面是不是觉得使用Spring MVC构建就是这么简单,其实这里得要多亏于Spring Boot的自动配置和起始依赖功能,否则并不会这么简单。由于Spring Boot的快速部署、快速构建等特点的突显,在现今的微服务中通常都会使用它作为项目基础来开发。
下面来讲解一下Spring MVC常用构建Restful的注解以及注解中的属性
- @Controller — 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象
- @RestController — 是@ResponseBody和@Controller组合注解,返回某种形式的文本信息,比如json、xml,配置的视图解析器InternalResourceViewResolver不起作用
- @RequestMapping — 用来处理请求地址映射的注解,可用于类或方法上
① value, method
value:指定请求的实际地址
method:指定请求的method类型, GET、POST、PUT、DELETE等;
② consumes,produces
consumes:指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
③ params,headers
params: 指定request中必须包含某些参数值是,才让该方法处理。
headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
备注:现在也可以不指定method,直接使用GetMapping、PostMapping、PutMapping以及DeleteMapping
@PathVariable — 用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数
@RequestParam — 主要用于在Spring MVC后台控制层获取参数,类似一种是request.getParameter(“name”),它有三个常用参数:defaultValue = “0”, required = false, value = “isApp”;defaultValue 表示设置默认值,required 通过boolean设置是否是必须要传入的参数,value 值表示接受的传入的参数类型。
@ResponseBody — 用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区
1.4.2.Spring Boot整合Jersey构建Restful API
(1)在项目中引入jersey start pom maven构建
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
(2)在Spring的启动类中,加入如下代码,注入Servlet Bean,配置Jersey的拦截url和相关配置类,这个ServletContainer其实就是一个HttpServlet类,跟Spring中的DispatcherServlet作用一样,是用来作为请求分发的入口:
@Bean
public ServletRegistrationBean jerseyServlet() {
ServletRegistrationBean registration = new ServletRegistrationBean(new ServletContainer(), "/rest/*");
// our rest resources will be available in the path /rest/*
registration.addInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS, JerseyConfiguration.class.getName());
return registration;
}
(3)Jersey相关配置类,配置扫描路径与Spring 请求范围
public class JerseyConfiguration extends ResourceConfig {
/**
* Instantiates a new Jersey configuration.
*/
public JerseyConfiguration() {
register(RequestContextFilter.class);
//配置restful package.
packages("com.happygo.dataacquisition.controller");
}
}
(4)创建一个Restful API的Resource类
@Path("/")
public class RestJerseyResource {
/**
* Hello map.
*
* @return the map
*/
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/hello")
public Map<String, Object> hello() {
Map<String, Object> map = new HashMap<>();
map.put("code", "1");
map.put("codeMsg", "success");
return map;
}
}
下面对Jersey注解作简单介绍:
- @Path — ①标注class,表明该类是个资源类。凡是资源类,必须使用@Path注解,不然jersey无法扫描到该资源类。
②标注method,表示具体的请求资源的路径
@GET — @GET、@POST、@PUT、@DELETE指明http请求的方式属于get,post,put,delete中的哪一种。具体的指定请求方式,需要在客户端发起请求指定
@Consumes — 指定http请求的MIME类型,表示任意的MIME类型。该注解的值是个数组类型,支持多个MIME类型,可以使用MediaType来指定MIME类型
@Produces — 指定http响应的MIME类型,表示任意的MIME类型。该注解的值是个数组类型,支持多个MIME类型,可以使用MediaType来指定MIME类型
@PathParam — 配合@Path来使用的,使用方式在@Path用{}来指定路径中匹配的参数,这种匹配是通过正则表达式来实现的。比如PersonResource使用@Path(“/{id}”)
@QueryParam — @QueryParam获取的参数,实际就是url中拼接在?后面的参数
@FormParam — 客户端以form(MIME为application/x-www-form-urlencoded)的方式提交表单,服务端使用@FormParam解析form表单中的参数
@FormDataParam — 通常在上传文件的时候,需要@FormDataParam。客户端提交form(MIME为multipart/form-data)的方式提交表单,服务端使用@FormDataParam来解析form表单中的参数
@HeaderParam — 获取http请求头中的参数值
@CookieParam — 获取http请求头中cookie中的参数值
关于jersey的详细文档请查阅Jersey官网文档