REST 是一种基于 HTTP 协议的架构风格,它把系统中的数据抽象为资源,通过统一的 URI 定位资源,再用 GET、POST、PUT、DELETE 等 HTTP 方法表达对资源的操作意图。Spring Boot 对 REST 的支持非常完善,只需要几个注解就能把一个普通 Java 类变成对外提供服务的 REST 控制器。这篇文章将完整演示如何用 Spring Boot 实现一套结构清晰的 REST 服务,涵盖接口设计、参数绑定、统一返回值、异常处理等常见环节。

一、搭建项目并理解 REST 的核心概念
首先创建一个 Spring Boot 项目,在 pom.xml 中引入 spring-boot-starter-web 依赖即可,它内部集成了内嵌 Tomcat 与 Spring MVC,不需要额外配置服务器。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>REST 的核心设计原则有三点:第一,一切皆资源,URI 用名词表示资源,例如 /api/users 表示用户集合,/api/users/1 表示 id 为 1 的用户;第二,用 HTTP 方法表达操作,GET 查询、POST 新增、PUT 全量更新、DELETE 删除;第三,无状态通信,每次请求都携带完整信息,服务端不依赖会话。很多初学者习惯写出 /getUserById?id=1、/deleteUser 这种接口,这属于典型的 RPC 风格,不符合 REST 语义。规范的写法应该让 URI 只描述资源,动作交给 HTTP 方法。
常见的 HTTP 状态码也要合理使用:200 表示成功、201 表示资源创建成功、204 表示删除成功无返回体、400 表示参数错误、404 表示资源不存在、500 表示服务器内部错误。正确使用状态码可以让客户端无需解析报文就能判断请求结果。
二、用 @RestController 实现 REST 接口
Spring Boot 提供了 @RestController 注解,它等价于 @Controller 加 @ResponseBody,方法返回值会自动序列化为 JSON 写入响应体。配合 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping 这组组合注解,可以直观地声明每个方法处理的 HTTP 方法和路径。
下面用一个用户管理接口演示完整的增删改查。先定义实体类和模拟的数据存储,再编写控制器。
@RestController
@RequestMapping("/api/users")
public class UserController {
// 使用线程安全的 Map 模拟数据库
private final Map<Long, User> userMap = new ConcurrentHashMap<>();
private final AtomicLong idGen = new AtomicLong(0);
// 查询列表:GET /api/users
@GetMapping
public List<User> list() {
return new ArrayList<>(userMap.values());
}
// 查询单个:GET /api/users/1
@GetMapping("/{id}")
public User getById(@PathVariable Long id) {
User user = userMap.get(id);
if (user == null) {
throw new BusinessException("用户不存在");
}
return user;
}
// 新增:POST /api/users
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User create(@RequestBody User user) {
long id = idGen.incrementAndGet();
user.setId(id);
userMap.put(id, user);
return user;
}
// 更新:PUT /api/users/1
@PutMapping("/{id}")
public User update(@PathVariable Long id, @RequestBody User user) {
if (!userMap.containsKey(id)) {
throw new BusinessException("用户不存在");
}
user.setId(id);
userMap.put(id, user);
return user;
}
// 删除:DELETE /api/users/1
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) {
userMap.remove(id);
}
}参数绑定方面有几个常用注解需要区分:@PathVariable 用于提取路径中的占位符,例如 /api/users/1 中的 1;@RequestBody 用于把请求体的 JSON 反序列化为 Java 对象,通常用在 POST 和 PUT 请求上;@RequestParam 用于接收查询参数,例如 ?page=1&size=10。如果参数是简单类型且不加注解,Spring MVC 默认也按请求参数名匹配绑定,但显式声明注解的可读性更好。
注意 @ResponseStatus 注解的用法:新增成功返回 201,删除成功返回 204,这是让状态码语义化的最简方式。如果不加该注解,方法正常返回时默认都是 200。
三、统一响应格式与全局异常处理
实际项目中直接返回实体类并不够,前端通常希望所有接口遵循统一的响应结构,包含状态码、提示信息和数据体三部分。可以定义一个通用的 Result 包装类,配合 @RestControllerAdvice 实现全局异常捕获。
// 统一响应结构
public class Result<T> {
private int code;
private String message;
private T data;
public static <T> Result<T> success(T data) {
Result<T> r = new Result<>();
r.code = 0;
r.message = "success";
r.data = data;
return r;
}
public static <T> Result<T> fail(int code, String message) {
Result<T> r = new Result<>();
r.code = code;
r.message = message;
return r;
}
// 省略 getter 和 setter
}
// 自定义业务异常
public class BusinessException extends RuntimeException {
public BusinessException(String message) {
super(message);
}
}
// 全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusiness(BusinessException e) {
return Result.fail(1001, e.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValid(MethodArgumentNotValidException e) {
String msg = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining("; "));
return Result.fail(1002, msg);
}
@ExceptionHandler(Exception.class)
public Result<Void> handleOther(Exception e) {
return Result.fail(500, "服务器内部错误");
}
}@RestControllerAdvice 会拦截所有控制器抛出的异常,根据 @ExceptionHandler 声明的异常类型分发到对应方法处理,这样控制器里就不需要写一堆 try-catch,代码会干净很多。需要提醒的是,最后一个兜底的 Exception 处理器里不要把原始异常信息直接返回给客户端,容易泄露堆栈等敏感信息,记录日志时可以用 log.error 输出完整堆栈供排查。
数据校验可以配合 spring-boot-starter-validation 使用,在实体类字段上加 @NotBlank、@Email 等注解,在控制器参数前加 @Valid,校验失败会抛出 MethodArgumentNotValidException,正好被上面的全局处理器捕获并转换成统一格式。
四、接口测试与常见问题
接口写完后,推荐使用 Postman、Apifox 或 IDEA 自带的 HTTP Client 进行测试。也可以在项目中引入 spring-boot-starter-test,使用 MockMvc 编写自动化测试:
@SpringBootTest
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void testCreateUser() throws Exception {
String body = "{\"name\":\"张三\",\"email\":\"zhangsan@ipipp.com\"}";
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(body))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.name").value("张三"));
}
}调试过程中有几个高频问题值得注意。一是中文乱码,可以在配置文件中设置 spring.http.encoding.force=true(新版本对应 server.servlet.encoding.force=true)。二是前后端分离项目遇到跨域报错,最简单的办法是在启动类或配置类上加 @CrossOrigin,或者定义全局的 WebMvcConfigurer 重写 addCorsMappings 方法统一放行。三是日期字段序列化格式,默认会输出时间戳或 ISO 字符串,可以在实体字段上加 @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") 定制。
总结一下,用 Spring Boot 实现 REST 服务的核心路径是:引入 web 依赖、用 @RestController 与组合注解声明接口、用 @PathVariable 和 @RequestBody 完成参数绑定、通过统一响应结构与全局异常处理保证输出一致性,最后借助 MockMvc 或接口工具完成验证。掌握这套流程后,再扩展分页查询、参数校验、接口文档(如集成 Knife4j)等能力都会水到渠成。
Spring BootRESTRESTful接口修改时间:2026-09-14 06:12:39