RESTful API 学习笔记
RESTful 介绍
REST 只是软件设计风格,不是强制标准,所以网上有大量最佳实践、设计指南,但是不存在官方强制设计标准。
为什么使用 RESTful
早期 Web 项目大多是前后端耦合(PHP、JSP)。 随着移动互联网发展,客户端多样化:Web网页、iOS、Android;还有各类开放平台(微信、微博开放平台),需要一套统一接口,给多端客户端提供数据服务,RESTful 就是为此而生。
REST:Representational State Transfer,表述性状态转移。是一套基于HTTP协议的网络应用设计风格。
核心思想:
– URL(URI)使用名词,用来定位资源
– HTTP动词描述对资源的操作(增删改查),不要把动作写在URL路径里。
HTTP 5个常用动词
| 动词 | 作用 | 对应CRUD | 说明 |
|---|---|---|---|
GET |
获取资源 | Read | 查询,不会修改服务器数据 |
POST |
新增资源 | Create | 创建新资源 |
DELETE |
删除资源 | Delete | 删除指定资源 |
PUT |
全量更新资源 | Update | 传递完整对象,缺失字段会被清空 |
PATCH |
局部更新资源 | Update | 只传需要修改的字段,只更新部分属性,PUT的补充 |
PUT 与 PATCH 的区别
- PUT:要求传递完整资源对象,如果缺少字段,理论后端要把缺失字段清空;适合完整替换资源。
- PATCH:只传要修改的少量字段,后端只更新传入的属性,不会触碰其他字段;适合局部修改。
幂等性
HTTP1.1定义:多次执行完全相同请求,对服务器产生的副作用和执行一次完全一样,就是幂等。
| 动词 | 是否幂等 | 说明 |
|---|---|---|
| GET | ✅幂等 | 查询,不会改变数据,调用多少次结果副作用都一样 |
| DELETE | ✅幂等 | 删除id=1,调用1次和调用N次,最终效果都是id=1不存在 |
| PUT | ✅幂等 | 全量替换资源,多次请求最终资源状态一致 |
| PATCH | ❌非幂等 | 比如数量 += 331,多次调用会重复累加,结果不一样 |
| POST | ❌非幂等 | 多次调用会创建多条新数据 |
面试常考:PUT是幂等;PATCH、POST不是幂等。
URI设计规范(重点)
URI用来定位资源(名词),URI里面不能出现动词;操作交给HTTP Method。
❌错误(URL里面带动词)
| 错误写法 | 问题 |
|---|---|
GET /api/getDogs |
url出现get查询动作 |
POST /api/addDogs |
url出现add新增动作 |
POST /api/editDogs |
url出现edit修改动作 |
GET /api/deleteDogs?id=2 |
url出现delete删除动作 |
✅正确写法(名词+http动词表达动作)
| 请求方式 | URL | 含义 |
|---|---|---|
| GET | /api/dogs |
获取全部狗狗列表 |
| POST | /api/dogs |
新增一条狗狗 |
| PUT | /api/dogs/1 |
全量更新id=1的狗狗 |
| DELETE | /api/dogs/1 |
删除id=1的狗狗 |
学生资源示例
| 请求方式 | URL | 业务含义 |
|---|---|---|
| GET | /api/students |
获取全部学生列表 |
| GET | /api/students/1 |
获取id=1学生详情 |
| POST | /api/students |
新增学生 |
| DELETE | /api/students/1 |
删除id=1学生 |
| PUT | /api/students/1 |
全量更新id=1学生信息 |
同一个URL,不同HTTP请求方式,映射后端不同接口方法。
充分利用HTTP协议特征
REST API 不只是把HTTP当作单纯传输通道,要充分利用HTTP协议本身能力:
- 看URI(名词):知道访问什么资源(定位)
- 看HTTP Method:知道要做什么操作(动作)
- 看HTTP Status状态码:知道请求执行结果
- 返回数据格式:主流使用JSON,也可支持XML等格式
常见HTTP状态码
| 状态码 | 含义 | 场景 |
|---|---|---|
| 200 OK | 请求成功 | GET查询、成功更新 |
| 201 Created | 创建成功 | POST新增资源成功 |
| 204 No Content | 无返回内容 | DELETE删除成功 |
| 400 Bad Request | 客户端参数错误 | 参数格式错误、校验失败 |
| 401 Unauthorized | 未认证 | 未登录、token失效 |
| 403 Forbidden | 权限不足 | 登录成功但是没有访问该资源权限 |
| 404 Not Found | 资源不存在 | url或者id对应的资源找不到 |
| 500 Internal Server Error | 服务器内部异常 | 代码报错、数据库异常 |
业务交互三种结果:
- 成功:2xx
- 客户端出错:4xx(参数错误、没登录、没权限、资源不存在)
- 服务端出错:5xx(后端代码、数据库异常)
实际开发中,除了返回http状态码,响应体内部一般自定义业务码、message提示,方便前端解析业务错误。
SpringBoot代码实现RESTful接口
Controller说明
@RestController:组合注解,@Controller + @ResponseBody,返回JSON数据。@GetMapping:等价@RequestMapping(method = RequestMethod.GET)@PostMapping:对应POST新增@PutMapping:对应PUT全量更新@DeleteMapping:对应DELETE删除@PathVariable:获取url路径上的变量/students/{id}@RequestBody:读取请求体JSON,接收前端提交的JSON对象。
注意:浏览器表单只支持GET、POST;PUT / DELETE 一般使用Postman、Apifox等接口测试工具测试。
Controller完整示例
import org.springframework.web.bind.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
@RestController
@RequestMapping("/api/students")
public class StudentRestController {
@Autowired
private IStudentService studentService;
/**
* GET /api/students 查询全部学生
*/
@GetMapping
public Result selectAll() {
List<Student> list = studentService.list();
return Result.ok(list);
}
/**
* GET /api/students/{id} 根据id查询单个学生
*/
@GetMapping("/{id}")
public Result selectById(@PathVariable("id") Integer id) {
Student student = studentService.getById(id);
return Result.ok(student);
}
/**
* POST /api/students 新增学生
* 请求体JSON接收对象 @RequestBody
*/
@PostMapping
public Result add(@RequestBody Student student) {
studentService.save(student);
return Result.ok("添加成功");
}
/**
* DELETE /api/students/{id} 删除学生
*/
@DeleteMapping("/{id}")
public Result deleteById(@PathVariable("id") Integer id) {
studentService.removeById(id);
return Result.ok("删除成功");
}
/**
* PUT /api/students/{id} 全量更新学生
*/
@PutMapping("/{id}")
public Result update(@PathVariable Integer id, @RequestBody Student student) {
student.setId(id);
studentService.updateById(student);
return Result.ok("更新成功");
}
/**
* PATCH /api/students/{id} 局部更新(只更新部分字段)
*/
@PatchMapping("/{id}")
public Result patchUpdate(@PathVariable Integer id, @RequestBody Student student) {
student.setId(id);
studentService.updateById(student);
return Result.ok("局部更新成功");
}
}
vue中的api为user/${id}
统一返回结果 Result工具类(前后端分离通用)
import lombok.Data;
@Data
public class Result {
private Integer code; //业务码 200成功,500失败
private String msg;
private Object data;
public static Result ok(Object data){
Result r = new Result();
r.setCode(200);
r.setMsg("操作成功");
r.setData(data);
return r;
}
public static Result fail(String msg){
Result r = new Result();
r.setCode(500);
r.setMsg(msg);
return r;
}
}
实际开发常见问题
- 浏览器表单不支持PUT、DELETE、PATCH请求
前端Vue/axios可以直接发送;传统form表单需要配置过滤器HiddenHttpMethodFilter。
- REST不是教条,复杂业务可以变通
- 登录、导出、批量操作等业务,很难单纯用名词+http动词表达,可以使用POST,不必硬套REST。
- 业界很多项目是REST风格 + RPC风格混合。
- 查询过滤、分页、排序
不要写在路径中,用url查询参数。 示例: GET /api/students?page=1&size=10&name=张三&sort=age,desc
- 版本控制
两种方案:
- 方式1:url携带版本号:
/api/v1/students(简单直观,项目常用) - 方式2:http请求头携带版本号。
面试简答
- 什么是RESTful?
REST是表述性状态转移,是一套API设计风格,不是标准;URL使用名词定位资源,HTTP动词(GET/POST/PUT/DELETE/PATCH)表达增删改查操作,充分利用HTTP状态码。
- PUT 和 PATCH区别?
PUT是全量更新,需要完整对象;PATCH局部更新,只传修改字段。PUT幂等,PATCH非幂等。
- 什么是幂等性?
多次相同请求,对服务器产生的副作用和一次请求一样;GET、PUT、DELETE幂等,POST、PATCH非幂等。
- URI为什么不能写动词?
URI负责定位资源,动作交给HTTP动词;如果URL写getUser、addUser就违背REST面向资源的设计思想。