Spring Boot Web开发篇:构建RESTful API

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原则,具有以下特点:

  1. 无状态性:每个请求都包含处理该请求所需的全部信息,服务器不会保存客户端的状态信息。
  2. 统一接口:通过标准的HTTP方法(GET、POST、PUT、DELETE等)操作资源。
  3. 资源导向:将系统中的各种概念抽象为资源,每个资源都有唯一的标识符(URI)。
  4. 可缓存性:响应可以被标记为可缓存或不可缓存,以提高性能。
  5. 分层系统:客户端通常无法知道是否直接连接到终端服务器,还是通过中介连接。

创建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进行测试:

  1. 创建用户:

    POST http://localhost:8080/api/users
    Content-Type: application/json

    {
    "username": "张三",
    "password": "123456",
    "email": "zhangsan@example.com",
    "age": 25
    }

  2. 获取用户:

    GET http://localhost:8080/api/users/1

  3. 获取所有用户:

    GET http://localhost:8080/api/users

  4. 更新用户:

    PUT http://localhost:8080/api/users/1
    Content-Type: application/json

    {
    "username": "张三丰",
    "password": "123456",
    "email": "zhangsanfeng@example.com",
    "age": 30
    }

  5. 删除用户:

    DELETE http://localhost:8080/api/users/1

  6. 搜索用户:

    GET http://localhost:8080/api/users/search?keyword=张

RESTful API设计最佳实践

  1. 使用名词表示资源:URI应该使用名词而不是动词来表示资源,如/users而不是/getUsers。

  2. 使用HTTP方法表示操作

    • GET:查询资源
    • POST:创建资源
    • PUT:更新资源(全量更新)
    • PATCH:更新资源(部分更新)
    • DELETE:删除资源
  3. 合理使用HTTP状态码

    • 200 OK:请求成功
    • 201 Created:创建成功
    • 400 Bad Request:请求参数错误
    • 401 Unauthorized:未授权
    • 404 Not Found:资源不存在
    • 500 Internal Server Error:服务器内部错误
  4. 版本控制:在URI中包含API版本号,如/api/v1/users。

  5. 统一响应格式:所有API响应应该遵循统一的格式,便于客户端解析。

  6. 参数验证:对所有输入参数进行验证,防止非法数据进入系统。

  7. 异常处理:统一处理系统异常,避免敏感信息泄露。

  8. 文档化:提供详细的API文档,方便开发者使用。

总结

本文详细介绍了如何使用Spring Boot构建RESTful API,包括:

  1. RESTful API的基本概念和设计原则
  2. Spring Boot Web项目的创建和配置
  3. 统一响应结果的封装
  4. 实体类的设计和参数验证
  5. 服务层的实现
  6. 控制器层的开发
  7. 全局异常处理
  8. 跨域配置
  9. API测试方法
  10. RESTful API设计最佳实践

通过以上内容的学习和实践,我们可以构建出高质量、规范化的RESTful API,为前端应用和第三方系统提供稳定可靠的数据接口。在下一篇文章中,我们将介绍Spring Boot安全篇:集成Spring Security实现认证授权。

作者:CSDN博客助手

版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。

相关推荐
软件架构师-叶秋7 小时前
spring boot入门篇之开发环境搭建
java·spring boot·后端
yume_sibai8 小时前
TS 常用内置方法
前端·javascript·typescript
新知图书8 小时前
ArkTS语言、基本组成与数据类型
前端·javascript·typescript
嘗_8 小时前
手写自己的小型react
前端·javascript·react.js
嘀咕博客8 小时前
h5游戏免费下载:HTML5拉杆子过关小游戏
前端·游戏·html5
Moonbit8 小时前
MoonBit 推出 LLVM Debugger,核心用户数破十万
前端·编程语言·llvm
zuo-yiran9 小时前
vue div标签可输入状态下实现数据双向绑定
前端·javascript·vue.js
qq_316837759 小时前
使用leader-line-vue 时垂直元素间距过小连线打转的解决
前端·javascript·vue.js
天天向上10249 小时前
vue3使用ONLYOFFICE 实现在线Word,Excel等文档
前端·javascript·html