Spring Boot Web开发篇:构建RESTful API
在前面的文章中,我们学习了Spring Boot的基础知识、配置管理以及如何整合MyBatis进行数据访问操作。在实际的Web应用开发中,构建一套规范、易用的RESTful API是至关重要的。RESTful API不仅能够为前端应用提供数据支持,还能为移动端、第三方系统等提供统一的数据接口。
本文将详细介绍如何使用Spring Boot构建高质量的RESTful API,包括控制器设计、请求处理、参数验证、异常处理等关键内容。
RESTful API简介
REST(Representational State Transfer)是一种软件架构风格,它定义了一组约束和原则,用于创建Web服务。RESTful API遵循REST原则,具有以下特点:
- 无状态性:每个请求都包含处理该请求所需的全部信息,服务器不会保存客户端的状态信息。
- 统一接口:通过标准的HTTP方法(GET、POST、PUT、DELETE等)操作资源。
- 资源导向:将系统中的各种概念抽象为资源,每个资源都有唯一的标识符(URI)。
- 可缓存性:响应可以被标记为可缓存或不可缓存,以提高性能。
- 分层系统:客户端通常无法知道是否直接连接到终端服务器,还是通过中介连接。
创建Spring Boot Web项目
首先,我们需要创建一个支持Web开发的Spring Boot项目。在pom.xml中添加以下依赖:
xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.7.0
com.example
springboot-restful-api
1.0.0
springboot-restful-api
Spring Boot构建RESTful API示例
8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-validation
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
设计统一响应结果
在构建RESTful API时,我们需要定义统一的响应格式,以便客户端能够正确解析响应结果。创建一个通用的结果封装类:
java
package com.example.common;
import java.io.Serializable;
public class Result implements Serializable {
private static final long serialVersionUID = 1L;
private int code;
private String message;
private T data;
public Result() {}
public Result(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static Result success(T data) {
return new Result<>(200, "操作成功", data);
}
public static Result success(String message, T data) {
return new Result<>(200, message, data);
}
public static Result error(String message) {
return new Result<>(500, message, null);
}
public static Result error(int code, String message) {
return new Result<>(code, message, null);
}
// Getter和Setter方法
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
public String toString() {
return "Result{" +
"code=" + code +
", message='" + message + ''' +
", data=" + data +
'}';
}
}
创建实体类
以用户实体为例,创建User类:
java
package com.example.entity;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Min;
import java.io.Serializable;
import java.time.LocalDateTime;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Long id;
@NotBlank(message = "用户名不能为空")
private String username;
@NotBlank(message = "密码不能为空")
private String password;
@Email(message = "邮箱格式不正确")
private String email;
@Min(value = 0, message = "年龄不能小于0")
private Integer age;
private LocalDateTime createTime;
private LocalDateTime updateTime;
// 构造方法
public User() {}
public User(String username, String password, String email, Integer age) {
this.username = username;
this.password = password;
this.email = email;
this.age = age;
this.createTime = LocalDateTime.now();
this.updateTime = LocalDateTime.now();
}
// Getter和Setter方法
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + ''' +
", password='" + password + ''' +
", email='" + email + ''' +
", age=" + age +
", createTime=" + createTime +
", updateTime=" + updateTime +
'}';
}
}
创建服务层
创建UserService接口和实现类:
java
package com.example.service;
import com.example.entity.User;
import java.util.List;
public interface UserService {
User createUser(User user);
User getUserById(Long id);
List getAllUsers();
User updateUser(Long id, User user);
void deleteUser(Long id);
List searchUsers(String keyword);
}
java
package com.example.service.impl;
import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
@Service
public class UserServiceImpl implements UserService {
// 使用内存存储模拟数据库
private static final ConcurrentHashMap userMap = new ConcurrentHashMap<>();
private static final AtomicLong idGenerator = new AtomicLong(1);
@Override
public User createUser(User user) {
Long id = idGenerator.getAndIncrement();
user.setId(id);
userMap.put(id, user);
return user;
}
@Override
public User getUserById(Long id) {
return userMap.get(id);
}
@Override
public List getAllUsers() {
return new ArrayList<>(userMap.values());
}
@Override
public User updateUser(Long id, User user) {
if (userMap.containsKey(id)) {
user.setId(id);
userMap.put(id, user);
return user;
}
return null;
}
@Override
public void deleteUser(Long id) {
userMap.remove(id);
}
@Override
public List searchUsers(String keyword) {
return userMap.values().stream()
.filter(user -> user.getUsername().contains(keyword) ||
user.getEmail().contains(keyword))
.collect(Collectors.toList());
}
}
创建控制器
创建UserController来处理用户相关的RESTful API请求:
java
package com.example.controller;
import com.example.common.Result;
import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import java.util.List;
@RestController
@RequestMapping("/api/users")
@Validated
public class UserController {
@Autowired
private UserService userService;
/**
* 创建用户
*/
@PostMapping
public Result createUser(@Valid @RequestBody User user) {
User createdUser = userService.createUser(user);
return Result.success("用户创建成功", createdUser);
}
/**
* 根据ID获取用户
*/
@GetMapping("/{id}")
public Result getUserById(@Min(value = 1, message = "用户ID必须大于0")
@PathVariable Long id) {
User user = userService.getUserById(id);
if (user == null) {
return Result.error(404, "用户不存在");
}
return Result.success("查询成功", user);
}
/**
* 获取所有用户
*/
@GetMapping
public Result> getAllUsers() {
List users = userService.getAllUsers();
return Result.success("查询成功", users);
}
/**
* 更新用户
*/
@PutMapping("/{id}")
public Result updateUser(@Min(value = 1, message = "用户ID必须大于0")
@PathVariable Long id,
@Valid @RequestBody User user) {
User updatedUser = userService.updateUser(id, user);
if (updatedUser == null) {
return Result.error(404, "用户不存在");
}
return Result.success("用户更新成功", updatedUser);
}
/**
* 删除用户
*/
@DeleteMapping("/{id}")
public Result deleteUser(@Min(value = 1, message = "用户ID必须大于0")
@PathVariable Long id) {
userService.deleteUser(id);
return Result.success("用户删除成功");
}
/**
* 搜索用户
*/
@GetMapping("/search")
public Result> searchUsers(@RequestParam String keyword) {
List users = userService.searchUsers(keyword);
return Result.success("查询成功", users);
}
}
全局异常处理
为了统一处理API中的异常,我们需要创建一个全局异常处理器:
java
package com.example.exception;
import com.example.common.Result;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.stream.Collectors;
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理参数验证异常(@Validated注解在类上时触发)
*/
@ExceptionHandler(ConstraintViolationException.class)
public Result handleConstraintViolationException(ConstraintViolationException e) {
String message = e.getConstraintViolations().stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.joining(", "));
return Result.error(400, message);
}
/**
* 处理参数验证异常(@Valid注解在方法参数上时触发)
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
return Result.error(400, message);
}
/**
* 处理参数绑定异常
*/
@ExceptionHandler(BindException.class)
public Result handleBindException(BindException e) {
String message = e.getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
return Result.error(400, message);
}
/**
* 处理自定义业务异常
*/
@ExceptionHandler(BusinessException.class)
public Result handleBusinessException(BusinessException e) {
return Result.error(e.getCode(), e.getMessage());
}
/**
* 处理其他未捕获的异常
*/
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
return Result.error(500, "系统内部错误:" + e.getMessage());
}
}
创建自定义业务异常类:
java
package com.example.exception;
public class BusinessException extends RuntimeException {
private int code;
private String message;
public BusinessException(String message) {
super(message);
this.code = 500;
this.message = message;
}
public BusinessException(int code, String message) {
super(message);
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
配置类
创建一个配置类来配置应用的相关设置:
java
package com.example.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
/**
* 配置跨域请求
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
主应用类
创建Spring Boot主应用类:
java
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringBootRestfulApiApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootRestfulApiApplication.class, args);
}
}
API测试
启动应用后,可以使用以下API进行测试:
-
创建用户:
POST http://localhost:8080/api/users
Content-Type: application/json{
"username": "张三",
"password": "123456",
"email": "zhangsan@example.com",
"age": 25
} -
获取用户:
-
获取所有用户:
-
更新用户:
PUT http://localhost:8080/api/users/1
Content-Type: application/json{
"username": "张三丰",
"password": "123456",
"email": "zhangsanfeng@example.com",
"age": 30
} -
删除用户:
-
搜索用户:
RESTful API设计最佳实践
-
使用名词表示资源:URI应该使用名词而不是动词来表示资源,如/users而不是/getUsers。
-
使用HTTP方法表示操作:
- GET:查询资源
- POST:创建资源
- PUT:更新资源(全量更新)
- PATCH:更新资源(部分更新)
- DELETE:删除资源
-
合理使用HTTP状态码:
- 200 OK:请求成功
- 201 Created:创建成功
- 400 Bad Request:请求参数错误
- 401 Unauthorized:未授权
- 404 Not Found:资源不存在
- 500 Internal Server Error:服务器内部错误
-
版本控制:在URI中包含API版本号,如/api/v1/users。
-
统一响应格式:所有API响应应该遵循统一的格式,便于客户端解析。
-
参数验证:对所有输入参数进行验证,防止非法数据进入系统。
-
异常处理:统一处理系统异常,避免敏感信息泄露。
-
文档化:提供详细的API文档,方便开发者使用。
总结
本文详细介绍了如何使用Spring Boot构建RESTful API,包括:
- RESTful API的基本概念和设计原则
- Spring Boot Web项目的创建和配置
- 统一响应结果的封装
- 实体类的设计和参数验证
- 服务层的实现
- 控制器层的开发
- 全局异常处理
- 跨域配置
- API测试方法
- RESTful API设计最佳实践
通过以上内容的学习和实践,我们可以构建出高质量、规范化的RESTful API,为前端应用和第三方系统提供稳定可靠的数据接口。在下一篇文章中,我们将介绍Spring Boot安全篇:集成Spring Security实现认证授权。
作者:CSDN博客助手
版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。