REST(REpresentational State Transfer)
表现出状态转移,概念记住就好,实际上就是一组架构的约束条件和原则。就是一组约定,规范。不强制,可以不遵守,也可以遵守。描述的是在网络中client和server的一种交互形式。
RESTful
RESTful是指风格,指定是满足或者部分满足REST约束、规则的应用程序API就属于RESTfulAPI。
REST中的基本概念
Resource
资源:在REST中,网络中一切内容都被认为是一种资源,所谓资源,就是网络上的一个实体,或者说是网络上的一个具体信息,它可以是一段文本,一张图片,一个视频,一段语音等。
统一资源访问标志符(URI):用于访问网络上某一资源的路径标志符,能够唯一确定一个资源。
比如:
http://www.abc.com/employees 表示www.abc.com主机上的employees资源。
https://dss0.bdstatic.com/-0U0bnSm1A5BphGlnYG/tam-ogel/5d4e9b24-dcc5-483a-b6da-be1e9e621891.js 表示网络上的一个js文件资源。
Representation
Representation:表现形式,资源是一种信息实体,我们把资源具体表现出来的形式,叫做它的“表现层”(Representation)。
比如一段文本可以使用txt的形式表示、也可以使用json形式,xml形式,html形式,二进制等等形式表示。
在REST规范中,URI只代表资源,只定位资源,不代表表现形式。资源的具体表现形式,应该在HTTP请求头中用Accept头和Content-Type头,和在响应头中用Content-Type头指定。
Accept请求头代表客户端会接受何种表现形式的资源,服务端应该把资源按照这些形式返回。
Content-Type请求头表示请求传递到服务器的信息内容格式。
Content-Type响应头表示服务器返回给客户端的信息内容格式。
以前:
一个URI可能是
http://www.xxx.com/employees.json //在URI上表示资源的表现形式。
REST规范:
http://www.xxx.com/employees 然后在请求头指定相关表现形式。
Accept:application/json
Content-Type:application/json
State Transfer
State Transfer:状态转移,访问一个网站,一种资源,就代表了客户端与服务器的一个交互过程,势必涉及到数据和状态的变化。
互联网通信协议HTTP协议是一个无状态协议,这意味着所有状态都保存在服务端,如果客户端想要操作服务器,就必须使用某种手段,让服务器端发生状态转换。改变服务器资源的状态。
比如:
http://www.xxxx.com/users 代表xxxx网站上的用户资源。
新增用户:用户从无到有的一个资源状态转化。
修改用户:用户从一种状态变成另外一个状态的资源状态转化。
删除用户:用户从有到无的一个资源状态转化。
Uniform Interface
Uniform Interface:统一接口,REST要求,必须通过统一接口来对资源的各种操作,对于每个资源只能执行一组有限的操作。
以HTTP1.1为例:
- 7个HTTP的请求方法:GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS
- HTTP头信息(可自定义)。
- HTTP响应状态码(可自定义)。
这些就是HTTP1.1统一的接口。
REST还要求,对于这些资源的操作,其操作语义必须由http消息体之前的部分完全表达,不可将操作语义封装在HTTP消息体内部。
例子:
http://www.xxxx.com/users 代表xxxx网站上的用户资源。
以前:
新增用户:http://www.xxxx.com/users/addUser 用POST方法。
查询用户:http://www.xxxx.com/users/getUser?id=1
REST规范:
URI是统一资源定位符,只用于定位资源。所以应该是一个名词形式,不应该包含动词,比如addUser。的add。
新增用户:http://www.xxxx.com/users 然后使用POST请求方法。http://www.xxxx.com/users表示用户资源,POST请求方法表示对该资源进行新增操作。
查询用户:http://www.xxxx.com/users/1 http://www.xxxx.com/users/1 直接获取id为1的用户资源。
系统接口
很多情况下,需要把系统的功能作为服务层暴露外部的其他应用或者给移动端使用,就需要把系统中的服务使用接口暴露出去。一般分为公共接口和内部接口。
公共接口:比如天气查询服务接口,航班查询服务接口等。把接口暴露出来给我们使用。
内部接口:通常是在公司的内部应用之间使用,比如分布式或者微服务架构中,各个应用服务暴露出接口。给公司其他服务调用。但是外部不能调用。
RESTful接口设计
对于RESTful API的设计,一般分为三个步骤:
- 资源设计。确定资源。
- 动作设计。确定请求方式。
- 返回结果设计。确定返回结果(响应头、状态码、类型等)
- 用“/代表资源的层级关系”。
- 用“?”过滤资源。
- 通常会对api添加上版本号,用于保持api升级时旧版本的兼容性。比如http://www.xxx.com/v1/xxx 中的v1就是接口api版本。
资源设计
使用以前的方式完成需求:
- 获取用户列表:http://www.xxx.com/users/list.do?keyword=xxx&page=xxx&size=xx
- 获取某个用户:http://www.xxx.com/users/getUser.do?id=xxx
- 新增用户:http://www.xxx.com/users/addUser.do
- 删除用户:http://www.xxx.com/users/deleteUser.do
- 修改用户:http://www.xxx.com/users/updateUser.do
这种方式的问题:大量的接口方法,URL地址设计复杂,且需要在URL中表现出资源的表示形式和操作类型。
在RESTful风格接口中,每个网址代表一种资源,资源是物品,自然是一个名词,所以网址中不能有动词。只能有名词。而名词往往与数据库表名相对应。一般来说,数据库的表就是某种资源的集合体。所以URI中的资源也应该使用复数形式。
用户资源:http://www.xxx.com/users
角色资源:http://www.xxx.com/roles
动作设计
就是设计对该资源的操作类型。一般采用HTTP的请求接口表示。
GET :从服务器获取资源,一项或多项。
POST:在服务器新建资源。
PUT:在服务端更新资源(客户端提供改变后的完整资源),更新整个资源对象。比如用户表有name,sex,age只有这三个属性。update user set name=?,sex=?,age=? where id=? limit 1更新整个对象,就用PUT。
DELETE:删除资源。
PATCH:在服务端更新资源(客户端提供改变资源的部分个别属性),更新资源的部分属性。比如update user set name=? where id=? limit 1 只更新部分。
下面这俩个基本用不上
HEAD:获取一个资源的元数据。比如一个资源的hash值和最后修改时间等。
OPTIONS:或者客户端针对一个资源能够执行的操作。比如(GET/PUT等)
例子:
GET http://www.xxx.com/users?keyword=xx //获取用户列表。如果有条件筛选,可以使用“?”进行筛选。
POST http://www.xxx.com/users //新增用户
GET http://www.xxx.com/users/id //查询某个用户。用users/id的URI层级定位资源。
PUT http://www.xxx.com/users/id //修改某个用户,客户端提供该用户的全部信息。
PATCH http://www.xxx.com/users/id //修改某个用户的部分属性。客户端提供该用户的部分信息。
DELETE http://www.xxx.com/users/id //删除用户。
返回结果设计
REST规范,这个一般根据实际情况是否遵守。
GET http://www.xxx.com/users?keyword=xx //返回资源对象列表。(数组、集合)
POST http://www.xxx.com/users //返回新生成的资源对象。看实际情况,如果客户端无需使用新生成的资源,则可以不返回。
GET http://www.xxx.com/users/id //返回单个资源
PUT http://www.xxx.com/users/id //返回修改后的的完整资源对象。看实际情况,如果客户端无需使用新生成的资源,则可以不返回。
PATCH http://www.xxx.com/users/id //返回修改后的的完整资源对象。看实际情况,如果客户端无需使用新生成的资源,则可以不返回。
DELETE http://www.xxx.com/users/id //返回一个空文档。
状态码设计(这是REST规范的状态码,可以不遵守,使用自己的也行):
返回内容类型:
一个API可以返回JSON、XML甚至是HTML文档。建议JSON
以前通过URL来规定:
http://www.xxx.com/users.json
http://www.xxx.com/users.html
但是更建议使用Accept和Content-Type规定:
Accept请求头代表客户端会接受何种表现形式的资源,服务端应该把资源按照这些形式返回。
Content-Type请求头表示请求传递到服务器的信息内容格式。
Content-Type响应头表示服务器返回给客户端的信息内容格式。
实战 对User资源的访问
@RestController
@RequestMapping("/users")
public class UserController {
/**
* 获取用户列表
* 资源设计 确定资源/users
* 动作设计 确定方法GET
* 返回结果设计(类型,头信息,状态码) 用户集合,Content-Type:application/json , 状态码200
* @return
*/
@GetMapping
public List<User> users(){
ArrayList users = new ArrayList();
users.add(new User(1,"Mike",(byte) 1,18));
users.add(new User(2,"Alina",(byte) 2,23));
users.add(new User(3,"John",(byte) 1,15));
return users;
}
/**
* 获取某个用户
* 资源设计 确定资源/users/{id} //路径资源占位符。
* 动作设计 确定方法GET
* 返回结果设计(类型,头信息,状态码) 用户对象,Content-Type:application/json , 状态码200
* @param id
* @return
*/
@GetMapping(value = "/{id}")
//这里必须使用PathVariable注解,把路径资源变量赋值到对应的方法形参,可以指定路径资源变量名。具体使用方法这里不是侧重点。
//@PathVariable(value = "id") 会把URI资源中对应层级的值赋值给形参id。
public User getUser(@PathVariable(value = "id") Integer id){
return new User(id,"Mike",(byte)1,18);
}
/**
* 删除某个用户
* 资源设计 确定资源/users/{id} //路径资源占位符。
* 动作设计 确定方法DELETE
* 返回结果设计(类型,头信息,状态码) 空文档void,, 状态码204
* @param id
*/
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable(value = "id") Integer id, HttpServletResponse response){
System.out.println("成功删除id为" + id + "的用户");
//HttpServletResponse.SC_NO_CONTENT 是一个常量,204
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
/**
* 新增用户
* 资源设计 确定资源/users
* 动作设计 确定方法POST
* 返回结果设计(类型,头信息,状态码) 用户对象, Content-Type:application/json , 状态码201
* @param user
* @return
*/
@PostMapping
public User addUser(@RequestBody User user,HttpServletResponse response){
//主键通常用于自增。
user.setId(5);
System.out.println("新增用户成功");
response.setStatus(HttpServletResponse.SC_CREATED);
return user;
}
/**
* 修改用户。整个
* 资源设计 确定资源/users/{id}
* 动作设计 确定方法PUT
* 返回结果设计(类型,头信息,状态码) 用户对象, Content-Type:application/json , 状态码201
* @param user
* @param id
* @param response
* @return
*/
@PutMapping("{id}")
public User updateUser(@RequestBody User user,@PathVariable(value = "id") Integer id,HttpServletResponse response){
user.setId(id);
response.setStatus(HttpServletResponse.SC_CREATED);
System.out.println("修改用户(id=" + id + ")成功");
return user;
}
/**
* 修改用户名
* 资源设计 确定资源/users/{id}
* 动作设计 确定方法PATCH
* 返回结果设计(类型,头信息,状态码) 用户对象, Content-Type:application/json , 状态码201
* @param userName
* @param id
* @return
*/
@PatchMapping("{id}")
public User updateUserName(String userName,@PathVariable(value = "id") Integer id){
User user = new User(id,userName, (byte) 1,18);
return user;
}
}
对应的URI为:顺序由上往下:
http://localhost:9897/users GET
http://localhost:9897/users/1 GET
http://localhost:9897/users/1 DELETE
http://localhost:9897/users POST
http://localhost:9897/users/1 PUT
http://localhost:9897/users/1 PATCH
对于spring mvc要使用PUT DELETE PATCH方法要加相应过滤器配置。具体可查阅相关文章。
API规范参考例子:
https://api.github.com/
http://docs.jiguang.cn/jmessage/server/rest_api_im/
本文深入探讨REST(Representational State Transfer)的概念及其在API设计中的应用,详细讲解了资源、URI、表现形式、状态转移及统一接口的基本要素。并通过实战示例,展示了如何遵循RESTful风格设计接口,包括资源、动作和返回结果的设计,以及状态码和内容类型的规范。
1198

被折叠的 条评论
为什么被折叠?



