Rest 是一种规范,符合 Rest 的 Api 就是 Rest Api。简单的说就是可联网设备利用 HTTP 协议通过 GET、POST、DELETE、PUT、PATCH 来操作具有URI标识的服务器资源,返回统一格式的资源信息,包括 JSON、XML、CSV、ProtoBuf、其他格式。
建议使用基于 HTTPS 协议
建议使用 api.domain.com
在RESTful架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。
常用的动词包括了5个
如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。
建议使用 HTTP 的状态码
详细状态码
返回指定错误信息内容
{ "error":"err message" }
针对不同操作,服务器向用户返回的结果应该符合以下规范。
RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
以上信息主要来自RESTful API设计指南
本章节需要编写的是对一个用户的增删改查操作,如下表是一个非 RESTful 和 标准 RESTful 的对比表。
1)File > New > Project,如下图选择 Spring Initializr 然后点击 【Next】下一步
Spring Initializr
2)填写 GroupId(包名)、Artifact(项目名) 即可。点击 下一步 groupId=com.fishpro artifactId=restful
GroupId
Artifact
3)选择依赖 Spring Web Starter 前面打钩。
Spring Web Starter
4)项目名设置为 spring-boot-study-restful
spring-boot-study-restful
本次新增2个文件,其中UserController类中包括了对用户的4个操作增删改查。
public class UserDO { private Integer userId; private String userName; public Integer getUserId() { return userId; } public void setUserId(Integer userId) { this.userId = userId; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
/** * RESTful API 风格示例 对资源 user 进行操作 * 本示例没有使用数据库,也没有使用 service 类来辅助完成,所有操作在本类中完成 * */ @RestController @RequestMapping("/api/user") public class UserController { /** * 模拟一组数据 * */ private List<UserDO> getData(){ List<UserDO> list=new ArrayList<>(); UserDO userDO=new UserDO(); userDO.setUserId(1); userDO.setUserName("admin"); list.add(userDO); userDO=new UserDO(); userDO.setUserId(2); userDO.setUserName("heike"); list.add(userDO); userDO=new UserDO(); userDO.setUserId(3); userDO.setUserName("tom"); list.add(userDO); userDO=new UserDO(); userDO.setUserId(4); userDO.setUserName("mac"); list.add(userDO); return list; } /** * SELECT 查询操作,返回一个JSON数组 * 具有幂等性 * */ @GetMapping("/users") @ResponseStatus(HttpStatus.OK) public Object getUsers(){ List<UserDO> list=new ArrayList<>(); list=getData(); return list; } /** * SELECT 查询操作,返回一个新建的JSON对象 * 具有幂等性 * */ @GetMapping("/users/{id}") @ResponseStatus(HttpStatus.OK) public Object getUser(@PathVariable("id") String id){ if(null==id){ return null; } List<UserDO> list= getData(); UserDO userDO=null; for (UserDO user:list ) { if(id.equals(user.getUserId().toString())){ userDO=user; break; } } return userDO; } /** * 新增一个用户对象 * 非幂等 * */ @PostMapping("/users") @ResponseStatus(HttpStatus.CREATED) public Object addUser(@RequestBody UserDO user){ List<UserDO> list= getData(); list.add(user);//模拟向列表中增加数据 return user; } /** * 编辑一个用户对象 * 幂等性 * */ @PutMapping("/users/{id}") @ResponseStatus(HttpStatus.CREATED) public Object editUser(@PathVariable("id") String id,@RequestBody UserDO user){ List<UserDO> list= getData(); for (UserDO userDO1:list ) { if(id.equals(userDO1.getUserId().toString())){ userDO1=user; break; } } return user; } /** * 删除一个用户对象 * 幂等性 * */ @DeleteMapping("/users") @ResponseStatus(HttpStatus.NO_CONTENT) public Object deleteUser(@PathVariable("id") String id){ List<UserDO> list= getData(); UserDO userDO=null; for (UserDO user:list ) { if(id.equals(user.getUserId().toString())){ //删除用户 userDO=user; break; } } return userDO;//返回被删除的对象 } }
获取全部资源 获取所有用户
GET http://localhost:8086/api/user/users/
[ { "userId": 1, "userName": "admin" }, { "userId": 2, "userName": "heike" }, { "userId": 3, "userName": "tom" }, { "userId": 4, "userName": "mac" } ]
获取单个资源 获取用户
GET http://localhost:8086/api/user/users/3
{ "userId": 3, "userName": "tom" }
新增一个资源 新增一个用户 POST http://localhost:8086/api/user/users 请求参数
{ "userId": 4, "userName": "newname" }
编辑更新一个资源
POST http://localhost:8086/api/user/users 请求参数
{ "userId": 1, "userName": "editname" }
删除一个资源
DELETE http://localhost:8086/api/user/users 请求参数
{ "userId": 1 }
RESTful API 可以结合 @RestControllAdvie 做全局异常处理,可以使用自定义标签做日志拦截,可以做全局日志拦截,可以做自动数据验证等等。
RESTful API 固然很好但大多数互联网公司都没有按照其规则来设计。因为 REST 本来就是一种风格,并没有什么固定的规则来约束,基于过于理想的 RESTful API 只会付出更多的人力成本和时间成本。
使用 HTTP 的 GET\POST\PUT\DELETE 来区分操作资源,HTTP Method 本身就对外部不友好,是隐藏的方法,把动词加入到 url 中,反而清晰可见,简单易懂,为什么一定要用 url 来表示资源而不能加动词呢。
GET\POST\PUT\DELETE 的兼容性有待认证,首先是兼容老的系统,大部分 HTTP 应用是基于 GET/POST 来实现的。
这可能是人的理解问题,就看我们对资源的定义,太多的场景,如果使用 Restful API 规则行事,势必要把一个 API 拆分多个 API,框架多个 API 之间的状态又成了问题。
使用 HTTP Status 状态码是没有问题的,但使用不常见的状态码表示操作层面的含义,对开发不是很友好。 对返回集中定义 status、message 例如下面的 json 返回是很常见的返回,简单易懂。并没有什么不妥,但在 RESTful Api 拥护者眼里就是错误的格式,错误的返回。
{ "status":1, "message":"", "data":[] }
统一为 RESTful API 风格,强化要求做的完美,比如带来更多的时间成本、人力成本。沟通成本。
原文链接:https://www.cnblogs.com/fishpro/p/spring-boot-study-restful.html