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原则,具有以下特点:
- 无状态性:每个请求都包含处理该请求所需的全部信息,服务器不会保存客户端的状态信息。
- 统一接口:通过标准的HTTP方法(GET、POST、PUT、DELETE等)操作资源。
- 资源导向:将系统中的各种概念抽象为资源,每个资源都有唯一的标识符(URI)。
- 可缓存性:响应可以被标记为可缓存或不可缓存,以提高性能。
- 分层系统:客户端通常无法知道是否直接连接到终端服务器,还是通过中介连接。
创建Spring Boot Web项目
首先,我们需要创建一个支持Web开发的Spring Boot项目。在pom.xml中添加以下依赖:
4.0.0org.springframework.bootspring-boot-starter-parent2.7.0com.examplespringboot-restful-api1.0.0springboot-restful-apiSpring Boot构建RESTful API示例8org.springframework.bootspring-boot-starter-weborg.springframework.bootspring-boot-starter-validationorg.springframework.bootspring-boot-starter-testtestorg.springframework.bootspring-boot-maven-plugin设计统一响应结果
在构建RESTful API时,我们需要定义统一的响应格式,以便客户端能够正确解析响应结果。创建一个通用的结果封装类:
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;}@Overridepublic String toString() {return "Result{" +"code=" + code +", message='" + message + ''' +", data=" + data +'}';}
}
创建实体类
以用户实体为例,创建User类:
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;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + ''' +", password='" + password + ''' +", email='" + email + ''' +", age=" + age +", createTime=" + createTime +", updateTime=" + updateTime +'}';}
}
创建服务层
创建UserService接口和实现类:
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);
}
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);@Overridepublic User createUser(User user) {Long id = idGenerator.getAndIncrement();user.setId(id);userMap.put(id, user);return user;}@Overridepublic User getUserById(Long id) {return userMap.get(id);}@Overridepublic List getAllUsers() {return new ArrayList<>(userMap.values());}@Overridepublic User updateUser(Long id, User user) {if (userMap.containsKey(id)) {user.setId(id);userMap.put(id, user);return user;}return null;}@Overridepublic void deleteUser(Long id) {userMap.remove(id);}@Overridepublic List searchUsers(String keyword) {return userMap.values().stream().filter(user -> user.getUsername().contains(keyword) || user.getEmail().contains(keyword)).collect(Collectors.toList());}
}
创建控制器
创建UserController来处理用户相关的RESTful API请求:
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 {@Autowiredprivate UserService userService;/*** 创建用户*/@PostMappingpublic 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);}/*** 获取所有用户*/@GetMappingpublic 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中的异常,我们需要创建一个全局异常处理器:
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());}
}
创建自定义业务异常类:
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;}@Overridepublic String getMessage() {return message;}public void setMessage(String message) {this.message = message;}
}
配置类
创建一个配置类来配置应用的相关设置:
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 {/*** 配置跨域请求*/@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOriginPatterns("*").allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS").allowedHeaders("*").allowCredentials(true).maxAge(3600);}
}
主应用类
创建Spring Boot主应用类:
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
}
- 获取用户:
GET http://localhost:8080/api/users/1
- 获取所有用户:
GET http://localhost:8080/api/users
- 更新用户:
PUT http://localhost:8080/api/users/1
Content-Type: application/json{"username": "张三丰","password": "123456","email": "zhangsanfeng@example.com","age": 30
}
- 删除用户:
DELETE http://localhost:8080/api/users/1
- 搜索用户:
GET http://localhost:8080/api/users/search?keyword=张
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博客助手
版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。
