第6章 构建 RESTful 服务
6.1 RESTful 简介
6.2 构建 RESTful 应用接口
6.3 使用 Swagger 生成 Web API 文档
6.4 实战:实现 Web API 版本控制
6.2 构建 RESTful 应用接口
6.2.1 Spring Boot 对 RESTful 的支持
Spring Boot 提供的spring-boot-starter-web
组件完全支持开发 RESTful API,提供了与 REST 操作方式(GET、POST、PUT、DELETE)对应的注解:
- @GetMapping:处理 GET 请求,获取资源。
- @PostMapping:处理 POST 请求,新增资源。
- @PutMapping:处理 PUT 请求,更新资源。
- @DeleteMapping:处理 DELETE 请求,删除资源。
- @PatchMapping:处理 PATCH 请求,用于部分更新资源。
通过这些注解就可以在 Spring Boot 项目中轻松构建 RESTful 接口。其中比较常用的是 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping 四个注解。
使用 Spring Boot 开发 RESTful 接口非常简单,通过 @RestController 定义控制器,然后使用 @GetMapping 和 @PostMapping 等注解定义地址映射,实现相应的资源操作方法即可。
示例:
RESTfulController.java
package com.example.restfulproject.controller;
import com.example.restfulproject.model.User;
import org.springframework.web.bind.annotation.*;
/**
* RESTful 接口简单案例(增删改查)
*/
@RestController
public class RESTfulController {
@GetMapping(value = "/user/{id}")
public String getUserById(@PathVariable String id) {
return "getUserById:" + id;
}
@PostMapping(value = "/user")
public String save(@RequestBody User user) {
String name = user.getName();
return "save successed";
}
@PutMapping(value = "/user")
public String update(@RequestBody User user) {
return "update successed";
}
@DeleteMapping(value = "/user/{id}")
public String delete(@PathVariable String id) {
return "delete id:" + id;
}
/*
// 上面的注解是 @RequestMapping 注解的简化:
@RequestMapping(value = "/user/{id}", method = RequestMethod.GET)
public String getUserById(@PathVariable String id) {
return "getUserById:" + id;
}
@RequestMapping(value = "/user", method = RequestMethod.POST)
public String save(User user) {
return "save successed";
}
@RequestMapping(value = "/user", method = RequestMethod.PUT)
public String update(User user) {
return "update successed";
}
@RequestMapping(value = "/user/{id}", method = RequestMethod.DELETE)
public String delete(@PathVariable String id) {
return "delete id:" + id;
}
*/
}
6.2.2 Spring Boot 实现 RESTful API
1. 设计API
在 RESTful 架构中,每个网址代表一种资源,所以 URI 中建议不要包含动词,只包含名词即可,而且所用的名词往往与数据库的表名对应。
(1)接口定义
下表是用户管理模块的接口定义,实际项目的 RESTful API 文档要更详细,还会定义全部请求的数据结构体。
用户管理模块API说明
HTTP Method | 接口地址 | 接口说明 |
---|---|---|
POST | /user | 创建用户 |
GET | /user/id | 根据id获取用户信息 |
PUT | /user | 更新用户 |
DELETE | /user/id | 根据id删除对应的用户 |
上表中定义了用户管理模块的接口,根据 REST 的定义,我们将用户定义为一种资源,通过 POST、DELETE、PUT、GET 等 HTTP Method 实现对用户的增、删、改、查。
(2)状态码和提示信息定义
除了设计 URL 接口之外,还需要定义服务端向客户端返回的状态码和提示信息。详细状态码说明见下表。
用户管理模块状态码
说明:
状态码 | 状态说明 |
---|---|
200 | Ok,请求成功 |
201 | Created,新增成功 |
203 | Updated,修改成功 |
204 | Deleted,删除成功 |
除了定义用户管理相关的业务状态码之外,还需要定义通用的错误码,如 400 对应数据校验错误、401 对应数据无权限等。
用户管理模块错误码
说明:
错误码 | 错误说明 |
---|---|
400 | 数据验证错误 |
401 | 无权限 |
404 | 资源不存在 |
500 | 服务端错误 |
2. 实现用户管理接口
UserManagementController.java
package com.example.restfulproject.controller;
import com.example.restfulproject.comm.utils.JSONResult;
import com.example.restfulproject.model.User;
import org.springframework.web.bind.annotation.*;
/**
* Spring Boot 实现 RESTful API
*/
@RestController
@RequestMapping("/userManagement")
public class UserManagementController {
/**
* 用户新增
*/
@PostMapping(value = "/user")
public JSONResult save(@RequestBody User user) {
System.out.println("用户创建成功:" + user.getName());
return JSONResult.ok(201, "用户创建成功");
}
/**
* 用户修改
*/
@PutMapping(value = "/user")
public JSONResult update(@RequestBody User user) {
System.out.println("用户修改成功:" + user.getName());
return JSONResult.ok(203, "用户修改成功");
}
/**
* 用户删除
*/
@DeleteMapping(value = "/user/{userId}")
public JSONResult delete(@PathVariable String userId) {
System.out.println("用户删除成功:" + userId);
return JSONResult.ok(204, "用户删除成功");
}
/**
* 获取用户
*/
@GetMapping(value = "/user/{userId}")
public JSONResult queryUserById(@PathVariable String userId) {
User user = new User();
user.setId(userId);
user.setName("zhangsan");
user.setAge(20);
System.out.println("获取用户成功:" + userId);
return JSONResult.ok(200, "获取用户成功", user);
}
}
JSONResult.java
package com.example.restfulproject.comm.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
/**
* 定义数据处理类
*
* @Title:: JSONResult.java
* @Package com.example.webproject.comm.utils
* @Description: 自定义响应数据结构
* 200: 表示成功
* 500: 表示错误,错误信息在 msg 字段中
* 501: bean 验证错误,无论多少个错误都以 map 形式返回
* 502: 拦截器拦截到用户 token 出错
* 555: 异常抛出信息
* Copyright: Copyright (c) 2023
*
* @create: 2023-02-05 17:35:54
* @Modify: 2023-02-17 19:41:30
* @version V1.1
*/
public class JSONResult {
// 定义 jackson 对象 (关于jackson的使用详解请参考:https://juejin.cn/post/6844904166809157639)
private static final ObjectMapper MAPPER = new ObjectMapper();
// 响应业务状态
private Integer code;
// 响应消息
private String msg;
// 响应中的数据
private Object data;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public JSONResult() {
}
public JSONResult(Integer code, String msg, Object data) {
this.code = code;
this.msg = msg;
this.data = data;
}
public JSONResult(Object data) {
this.code = 200;
this.msg = "OK";
this.data = data;
}
// // 是否成功
// public Boolean isSuccess() {
// return this.code == 200;
// }
// 构建返回的数据
public static JSONResult build(Integer status, String msg, Object data) {
return new JSONResult(status, msg, data);
}
// 200:成功,有返回值
public static JSONResult ok(Object data) {
return new JSONResult(data);
}
// 200:成功,没有返回值
public static JSONResult ok() {
return new JSONResult(null);
}
//200:成功,没有返回值
public static JSONResult ok(Integer code, String msg) {
return new JSONResult(code, msg, null);
}
// 200: 成功
public static JSONResult ok(Integer code, String msg, Object data) {
return new JSONResult(code, msg, data);
}
//500:错误,错误信息在 msg 字段中
public static JSONResult errorMsg(String msg) {
return new JSONResult(500, msg, null);
}
//501:bean 验证错误,无论多少个错误都以 map 形式返回
public static JSONResult errorMap(Object data) {
return new JSONResult(501, "error", data);
}
//502:拦截器拦截到用户 token 出错
public static JSONResult errorTokenMsg(String msg) {
return new JSONResult(502, msg, null);
}
//555:异常抛出信息
public static JSONResult errorException(String msg) {
return new JSONResult(555, msg, null);
}
/**
* @Description: 将 json 结果集转化为 JSONResult 对象,需要转换的对象是一个类
*
* @param jsonData
* @param clazz
* @return
*/
public static JSONResult formatToPojo(String jsonData, Class<?> clazz) {
try {
if (clazz == null) {
return MAPPER.readValue(jsonData, JSONResult.class);
}
JsonNode jsonNode = MAPPER.readTree(jsonData);
JsonNode data = jsonNode.get("data");
Object obj = null;
if (clazz != null) {
if (data.isObject()) {
obj = MAPPER.readValue(data.traverse(), clazz);
} else if (data.isTextual()) {
obj = MAPPER.readValue(data.asText(), clazz);
}
}
return build(jsonNode.get("code").intValue(), jsonNode.get("msg").asText(), obj);
} catch (Exception e) {
return null;
}
}
/**
* @Description: 没有 object 对象的转化
*
* @param json
* @return
*/
public static JSONResult format(String json) {
try {
return MAPPER.readValue(json, JSONResult.class);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
/**
* @Description: Object 是集合转化,需要转换的对象是一个list
*
* @param jsonData
* @param clazz
* @return
*/
public static JSONResult formatToList(String jsonData, Class<?> clazz) {
try {
JsonNode jsonNode = MAPPER.readTree(jsonData);
JsonNode data = jsonNode.get("data");
Object obj = null;
if(data.isArray() && data.size() > 0) {
obj = MAPPER.readValue(data.traverse(), MAPPER.getTypeFactory().constructCollectionType(List.class,clazz));
}
return build(jsonNode.get("code").intValue(), jsonNode.get("msg").asText(), obj);
} catch (Exception e) {
return null;
}
}
}
3. 验证测试
(1)获取用户:http://localhost:8080/userManagement/user/2001
(2)用户新增:http://localhost:8080/userManagement/user
(3)用户修改:http://localhost:8080/userManagement/user
(4)用户删除:http://localhost:8080/userManagement/user/2001
来源:《Spring Boot 从入门到实战》学习笔记