0、REST介绍
还是先从百度百科的 rest 词条开始吧!
正因为 REST 是目前比较流行的软件架构,所以网上有大量介绍及开发设计规范等的文章,而我此篇文章基于实战的目的,对 REST 关键的几个方面做一个简单的总结,当然和网上的其它文章一样,都是仁者见仁、智者见智,仅供参考!
本文将从下面几个方面入手:
- URI命名
- HTTP方法
- HTTP 状态码
- 返回数据封装格式
- 接口文档
1、 URI 命名
1.1、命名规范
RFC 3986 定义了通用的URI语法:
URI = scheme “://” authority “/” path [ “?” query ][ “#” fragment ]
scheme: 指底层用的协议,如http、https、ftp
host: 服务器的IP地址或者域名
port: 端口,http中默认80
path: 访问资源的路径,就是咱们各种web 框架中定义的route路由
query: 为发送给服务器的参数
fragment: 锚点,定位到页面的资源,锚点为资源id
-
网址中只能有名词,不能用动词
在 RESTful 架构中,每个网址代表一种资源( resource ),所以网址中不能有动词,只能有名词(特殊情况可以使用动词),而且所用的名词往往与数据库的表格名对应
示例:
// 正确规范:
/users // 表示列举所有用户
// 错误规范:
/listUsers // 有动词
/getUsers // 有动词
- 不用大写
// 正确规范:
/users
// 错误规范:
/Users // URI 不能有大写字母
- 用中杠 - 不用下杠 _
// 正确规范:
/user-orders // 用户订单列表
// 错误规范:
/user_orders // 不使用下划线 _
- URI 中的名词表示资源集合,使用复数形式
// 正确规范:
/users // 复数表示集合
/users/1 // id 为 1 的用户信息
/users/1/orders // id 为 1 的用户的订单列表
错误规范:
/user // 不能用单数表示单个资源,需要用 id 来指定,参考 /user/1
-
避免层级过深的 URI
过深的 URI 不易维护,如 /users/1/orders/2/products/3 ,尽量使用查询参数代替路径中的实体导航,如 /products?user=1&order=1; - API 版本号要放入到 URI 中
// 正确规范:
/v1/users // 表示 API 版本为 v1
-
URL结尾不应该包含斜杠“/”
REST API不允许一个尾部的斜杠,不应该将它们包含在提供给客户端的链接的结尾处。
// 错误规范:
/v1/users/ // 末尾不能为“/”
-
资源过滤
在获取资源的时候,有可能需要获取某些“过滤”后的资源,例如指定前10行数据,如:
http://api.test.com/v1/users?page=1&page-size=10
下面是一些常用的过滤参数:
?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件
1.2、编写代码
为了简化,我们采用前面编写的演示工程的基础上进行代码编写。
用 Idea 打开 edu 演示工程,打开控制器 UseController 的代码:
上图四个框内的代码分别对应查、增、改、删四个操作的代码,下面进行 URI 规范的修改
-
增加版本
在都拿顶部的 @RestController 注解后面增加 @RequestMapping 注解,该注解对 UserController 控制器的所有 http 方法都起作用。假设 API 版本为 v1,修改结果如下:
1.2.1、修改“查询” URI
-
修改“查询” URI
上图中, @GetMapping 注解表示 http 方法为 GET ,注解的值为“/list",不规范,改为"/users"。
-
测试
运行项目 UserApplication,用 Postman 测试访问,采用 GET 方法:
OK!
1.2.2、修改“增加” URI
-
修改“增加” URI
上图中, @PostMapping 注解表示 http 方法为 POST ,注解的值为“/addUser",不规范,改为"/users"。
-
测试
重新运行 UserApplication ,用 Postman 测试访问,采用 POST 方法,并传递 username 和 password 两个参数:
1.2.3、修改“修改” URI
-
修改“修改” URI
上图中, @PutMapping 注解表示 http 方法为 PUT ,注解的值为“/updUser/{id}",不规范,改为"/users/{id}"。
-
测试
重新运行 UserApplication ,用 Postman 测试访问,采用 PUT 方法,并传递 username 和 password 两个参数:
OK!
1.2.4、修改“删除” URI
-
修改“删除” URI
上图中, @DeleteMapping 注解表示 http 方法为 DELETE,注解的值为“/delUser/{id}",不规范,改为"/users/{id}"。
-
测试
重新运行 UserApplication ,用 Postman 测试访问,采用 DELETE 方法:
OK!
2、HTTP方法
常用的方法有 GET , POST , PUT , DELETE ,此外有时还会用到HEAD、PATCH和OPTION。前面说我们系统的设计是以资源为核心的,URI的命名要使用名词,那我们要如何来表示一个操作动作呢?就是依靠HTTP方法,也叫HTTP动词。也就是通过这种动名词的组合来表示一次操作。
从上面修改 URI 的示例里,已经基本讲述和测试了 GET , POST , PUT , DELETE 四种方法:
GET - 获取数据集合,用到的 URI 为:/v1/users
POST - 新增数据,用到的 URI 为:/v1/users,和 GET 方法的 URI 相同
PUT - 修改数据,用到的 URI 为:/v1/users/{id}
DELETE - 删除数据,用到的 URI 为:/v1/users/{id},和 PUT 方法的 URI 相同
通过列举这四种方法,可以看到,采用规范后的 URI , 简单、明晰了很多!
注:其它 http 方法用得很少,如果有兴趣可自行搜索及练习,此处不再赘述!
3、HTTP 状态码
服务器向用户返回的状态码和提示信息,常见的有以下一些(方括号中是该状态码对应的HTTP动词)。
- 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
- 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
- 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
- 204 NO CONTENT - [DELETE]:用户删除数据成功。
- 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
- 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
- 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
- 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
- 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
- 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
- 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
- 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
更多 HTTP 状态码,请到网上搜索。
4、返回数据封装格式
RestAPI 返回的封装格式有很多种,网上也分化出很多阵营,公说公有理、婆说婆有理,所以还是要自己多练习、感受,做出和项目规模适应的封装格式。
-
最简格式
我们刚才在例子里用的就是最简格式,对于调试或较简单的接口,可以这么用,但不推荐。
[
{
"id": 1,
"username": "金庸",
"password": "123456"
},
{
"id": 2,
"username": "古龙",
"password": "654321"
}
]
-
自定义业务状态码封装格式
该模式和业务联系紧密,可灵活定制,推荐。
{
"status": 0,
"message": "操作成功!",
"data": [
{
"id": 1,
"username": "金庸",
"password": "123456"
},
{
"id": 2,
"username": "古龙",
"password": "654321"
}
],
extra: {
type: 1,
desc: "附加操作"
}
}
-
http 状态码 + 自定义业务状态码封装形式
该模式分为两层状态码,最外层是 http 状态码,里面一层是自定义业务状态码。在 http rest 模式下,该数据封装形式既可以满足 http 状态码的统一性,有可以兼顾到业务逻辑状态码的灵活定制、描述的准确性。我更推荐使用该模式。
{
"code": 200,
"msg": "OK",
{
"status": 0,
"message": "操作成功!",
"data": [
{
"id": 1,
"username": "金庸",
"password": "123456"
},
{
"id": 2,
"username": "古龙",
"password": "654321"
}
],
extra: {
type: 1,
desc: "附加操作"
}
}
}
5、 API 接口文档
在此,我们将使用 Springfox(Swagger) 来实现 ApiDoc 接口文档, Springfox 的前身是 Swagger-SpringMVC ,是一个开源的 API doc 框架,可以将我们的Controller的方法以文档的形式展现,基于Swagger。官网地址:http://springfox.github.io/springfox/
下面我们继续在演示项目 edu 上面来进行 API 接口文档的实现,如果这是您没看本系列前面的文章,请快速浏览一下前面的文章。
5.1、增加 Maven 依赖
我们在 microsvc 项目的 pom.xml 文件里添加下面的依赖:
<!--springfox-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.4.0</version>
</dependency>
5.2、修改 user 微服务的启动程序文件 - UserApplication.java
import 导入:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.context.request.async.DeferredResult;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
添加注解:
@EnableSwagger2
在启动类 UserApplication 里的 main 函数后面添加下面的代码:
@Bean
public Docket userApi() {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("user")
.genericModelSubstitutes(DeferredResult.class)
// .genericModelSubstitutes(ResponseEntity.class)
.useDefaultResponseMessages(false)
.forCodeGeneration(false)
.pathMapping("/")
.select()
.paths(PathSelectors.regex("/v1.*"))//过滤的接口
.build()
.apiInfo(userApiInfo());
}
private ApiInfo userApiInfo() {
Contact contact = new Contact("檀布施", "https://www.baidu.com", "tanbushi@qq.com");
ApiInfo apiInfo = new ApiInfo("用户 API 接口",//大标题
"用户 REST 风格 API",//小标题
"v1",//版本
"www.baidu.com",
contact,//作者
"主页",//链接显示文字
""//网站链接
);
return apiInfo;
}
修改完成后,文件内容如下图:
5.3、修改 user 微服务的控制器文件 - UserController.java
import 导入:
import com.tanbushi.edu.microsvc.user.entity.UserEntity;
import com.tanbushi.edu.microsvc.user.repository.UserRepository;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
在 UserController 类里的每一个 mapping 后面追加一句,下图红框里的内容:
@GetMapping(value = "/users")
@ApiOperation(value = "获取用户列表", notes = "获取当前条件下的用户列表", produces = "application/json")
@PostMapping(value = "/users")
@ApiOperation(value = "添加用户记录", notes = "添加一条用户记录,username、password 必填", produces = "application/json")
@PutMapping(value = "/users/{id}")
@ApiOperation(value = "修改用户记录", notes = "修改一条用户记录,usernme、password必填", produces = "application/json")
@DeleteMapping(value = "/users/{id}")
@ApiOperation(value = "删除用户记录", notes = "删除一条用户记录", produces = "application/json")
5.4、运行
重新编译运行 UserApplication,成功后,在浏览器里输入:http://127.0.0.1:8100/swagger-ui.html,可以访问到 API 文档了:
点击红框内的 user-controller, 就可以看的具体的接口说明了:
6、其它
- 消息体格式 - 推荐尽量采用 JSON 格式
- 鉴权 - 采用 Autho2.0
在此不再赘述
上一篇:Spring Cloud 零基础实战(五):访问 MySQL 数据库
下一篇:待续...
所属文集:Spring Cloud 零基础实战