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博客助手
版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。