概述
RESTful API 是现代 Web 应用开发的核心技术,基于 REST(Representational State Transfer)架构风格设计。Spring MVC 提供了强大的 RESTful API 开发支持,通过注解驱动的方式简化了 API 的构建过程。
核心特性
- 统一接口: 使用标准 HTTP 方法(GET、POST、PUT、DELETE)
- 无状态: 每个请求都包含处理所需的所有信息
- 可缓存: 响应可以被缓存以提高性能
- 分层系统: 支持代理、网关等中间层
- 按需代码: 可选的客户端代码下载
应用场景
| 场景 | 描述 | 示例 |
|---|---|---|
| 微服务架构 | 服务间通信接口 | 用户服务、订单服务、支付服务 |
| 移动端API | 移动应用后端接口 | iOS、Android 应用API |
| 前后端分离 | Web 前端数据接口 | Vue、React、Angular 应用 |
| 第三方集成 | 对外开放的数据接口 | 开放平台API、数据同步接口 |
💡 提示: RESTful API 是现代分布式系统的基础,掌握其设计和实现对于 Java Web 开发至关重要。
RESTful 架构基础
1. REST 设计原则
资源标识
# 良好的资源标识
GET /api/users # 获取用户列表
GET /api/users/123 # 获取特定用户
POST /api/users # 创建新用户
PUT /api/users/123 # 更新用户
DELETE /api/users/123 # 删除用户
# 嵌套资源
GET /api/users/123/posts # 获取用户的文章
POST /api/users/123/posts # 为用户创建文章
HTTP 状态码规范
| 状态码 | 含义 | 使用场景 |
|---|---|---|
| 2xx 成功 | ||
| 200 | OK | 成功返回数据 |
| 201 | Created | 资源创建成功 |
| 204 | No Content | 成功但无返回内容 |
| 4xx 客户端错误 | ||
| 400 | Bad Request | 请求参数错误 |
| 401 | Unauthorized | 未授权访问 |
| 403 | Forbidden | 禁止访问 |
| 404 | Not Found | 资源不存在 |
| 409 | Conflict | 资源冲突 |
| 5xx 服务器错误 | ||
| 500 | Internal Server Error | 服务器内部错误 |
| 503 | Service Unavailable | 服务不可用 |
2. 环境准备
项目依赖配置
<dependencies>
<!-- Spring MVC -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.21</version>
</dependency>
<!-- JSON 处理 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
<!-- 数据验证 -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.2.3.Final</version>
</dependency>
<!-- Servlet API -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.21</version>
<scope>test</scope>
</dependency>
</dependencies>
Spring MVC 配置
web.xml 配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>RESTful API Application</display-name>
<!-- 字符编码过滤器 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring MVC 前端控制器 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
</web-app>
spring-mvc.xml 配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 组件扫描 -->
<context:component-scan base-package="com.example.api"/>
<!-- 启用注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters>
<!-- JSON 消息转换器 -->
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
<value>text/json;charset=UTF-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- CORS 全局配置 -->
<mvc:cors>
<mvc:mapping path="/api/**"
allowed-origins="*"
allowed-methods="GET,POST,PUT,DELETE,OPTIONS"
allowed-headers="*"
allow-credentials="true"
max-age="3600"/>
</mvc:cors>
<!-- 静态资源处理 -->
<mvc:resources mapping="/static/**" location="/static/"/>
</beans>
Java 配置方式:
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example.api")
public class WebConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
// 配置 JSON 转换器
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
jsonConverter.setObjectMapper(objectMapper);
converters.add(jsonConverter);
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
JSON 数据处理
1. JSON 基础概念
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,具有以下特点:
- 易于阅读和编写
- 语言无关性
- 结构简单
JSON 数据结构
// 对象结构
{
"id": 1,
"username": "张三",
"email": "zhangsan@example.com",
"age": 25,
"active": true
}
// 数组结构
[
{
"id": 1,
"name": "用户1"
},
{
"id": 2,
"name": "用户2"
}
]
// 嵌套结构
{
"user": {
"id": 1,
"profile": {
"name": "张三",
"address": {
"city": "北京",
"district": "朝阳区"
}
},
"posts": [
{
"id": 1,
"title": "第一篇文章"
}
]
}
}
2. Java 实体类设计
package com.example.api.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.validation.constraints.*;
import java.time.LocalDateTime;
import java.util.List;
/**
* 用户实体类
*/
public class User {
@JsonProperty("id")
private Long id;
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 50, message = "用户名长度必须在3-50之间")
@JsonProperty("username")
private String username;
@Email(message = "邮箱格式不正确")
@JsonProperty("email")
private String email;
@JsonIgnore // 密码字段不参与JSON序列化
private String password;
@Min(value = 18, message = "年龄不能小于18岁")
@Max(value = 100, message = "年龄不能大于100岁")
@JsonProperty("age")
private Integer age;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonProperty("createTime")
private LocalDateTime createTime;
@JsonProperty("active")
private Boolean active;
@JsonProperty("roles")
private List<String> roles;
@JsonProperty("profile")
private UserProfile profile;
// 构造函数
public User() {
this.createTime = LocalDateTime.now();
this.active = true;
}
// 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;
}
// ... 其他 getter 和 setter
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", email='" + email + '\'' +
", age=" + age +
", createTime=" + createTime +
", active=" + active +
'}';
}
}
/**
* 用户档案实体类
*/
public class UserProfile {
@JsonProperty("realName")
private String realName;
@JsonProperty("phone")
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String phone;
@JsonProperty("address")
private String address;
// getter 和 setter
}
3. JSON 序列化配置
Jackson 配置
@Configuration
public class JacksonConfig {
@Bean
@Primary
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
// 配置日期格式
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
// 忽略未知属性
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 忽略空值字段
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 配置驼峰命名转换
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
// 处理时间
mapper.registerModule(new JavaTimeModule());
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
return mapper;
}
}
自定义序列化器
// 自定义日期序列化器
public class CustomDateSerializer extends JsonSerializer<Date> {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public void serialize(Date date, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(dateFormat.format(date));
}
}
// 使用自定义序列化器
public class User {
@JsonSerialize(using = CustomDateSerializer.class)
private Date createTime;
// ...
}
REST 控制器开发
1. 基础 REST 控制器
package com.example.api.controller;
import com.example.api.entity.User;
import com.example.api.service.UserService;
import com.example.api.dto.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
/**
* 用户 REST 控制器
*/
@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "*")
public class UserController {
@Autowired
private UserService userService;
/**
* 获取所有用户
*/
@GetMapping
public ResponseEntity<ApiResponse<List<User>>> getAllUsers(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String keyword) {
List<User> users = userService.findAll(page, size, keyword);
long total = userService.count(keyword);
ApiResponse<List<User>> response = ApiResponse.success(users);
response.setTotal(total);
response.setPage(page);
response.setSize(size);
return ResponseEntity.ok(response);
}
/**
* 根据ID获取用户
*/
@GetMapping("/{id}")
public ResponseEntity<ApiResponse<User>> getUserById(@PathVariable Long id) {
User user = userService.findById(id);
return ResponseEntity.ok(ApiResponse.success(user));
}
/**
* 创建新用户
*/
@PostMapping
public ResponseEntity<ApiResponse<User>> createUser(@Valid @RequestBody User user) {
User createdUser = userService.create(user);
return ResponseEntity.status(HttpStatus.CREATED)
.body(ApiResponse.success(createdUser));
}
/**
* 更新用户信息
*/
@PutMapping("/{id}")
public ResponseEntity<ApiResponse<User>> updateUser(
@PathVariable Long id,
@Valid @RequestBody User user) {
user.setId(id);
User updatedUser = userService.update(user);
return ResponseEntity.ok(ApiResponse.success(updatedUser));
}
/**
* 删除用户
*/
@DeleteMapping("/{id}")
public ResponseEntity<ApiResponse<Void>> deleteUser(@PathVariable Long id) {
userService.delete(id);
return ResponseEntity.ok(ApiResponse.success(null, "用户删除成功"));
}
/**
* 批量删除用户
*/
@DeleteMapping
public ResponseEntity<ApiResponse<Void>> deleteUsers(@RequestBody List<Long> ids) {
userService.deleteByIds(ids);
return ResponseEntity.ok(ApiResponse.success(null, "批量删除成功"));
}
/**
* 用户搜索
*/
@GetMapping("/search")
public ResponseEntity<ApiResponse<List<User>>> searchUsers(
@RequestParam String keyword,
@RequestParam(defaultValue = "username") String field) {
List<User> users = userService.search(keyword, field);
return ResponseEntity.ok(ApiResponse.success(users));
}
}
2. 统一响应格式
package com.example.api.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* 统一API响应格式
*/
public class ApiResponse<T> {
@JsonProperty("code")
private int code;
@JsonProperty("message")
private String message;
@JsonProperty("data")
private T data;
@JsonProperty("timestamp")
private long timestamp;
@JsonProperty("total")
private Long total;
@JsonProperty("page")
private Integer page;
@JsonProperty("size")
private Integer size;
public ApiResponse() {
this.timestamp = System.currentTimeMillis();
}
public static <T> ApiResponse<T> success(T data) {
ApiResponse<T> response = new ApiResponse<>();
response.setCode(200);
response.setMessage("成功");
response.setData(data);
return response;
}
public static <T> ApiResponse<T> success(T data, String message) {
ApiResponse<T> response = new ApiResponse<>();
response.setCode(200);
response.setMessage(message);
response.setData(data);
return response;
}
public static <T> ApiResponse<T> error(int code, String message) {
ApiResponse<T> response = new ApiResponse<>();
response.setCode(code);
response.setMessage(message);
return response;
}
// 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;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
public Long getTotal() {
return total;
}
public void setTotal(Long total) {
this.total = total;
}
public Integer getPage() {
return page;
}
public void setPage(Integer page) {
this.page = page;
}
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
}
HTTP 方法映射
1. 标准 HTTP 方法
@RestController
@RequestMapping("/api/products")
public class ProductController {
// GET - 获取资源
@GetMapping
public ResponseEntity<List<Product>> getAllProducts() {
// 获取所有产品
}
@GetMapping("/{id}")
public ResponseEntity<Product> getProduct(@PathVariable Long id) {
// 获取单个产品
}
// POST - 创建资源
@PostMapping
public ResponseEntity<Product> createProduct(@RequestBody Product product) {
// 创建新产品
}
// PUT - 完整更新资源
@PutMapping("/{id}")
public ResponseEntity<Product> updateProduct(
@PathVariable Long id,
@RequestBody Product product) {
// 完整更新产品
}
// PATCH - 部分更新资源
@PatchMapping("/{id}")
public ResponseEntity<Product> patchProduct(
@PathVariable Long id,
@RequestBody Map<String, Object> updates) {
// 部分更新产品
}
// DELETE - 删除资源
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteProduct(@PathVariable Long id) {
// 删除产品
}
// HEAD - 获取资源头信息
@RequestMapping(value = "/{id}", method = RequestMethod.HEAD)
public ResponseEntity<Void> checkProduct(@PathVariable Long id) {
// 检查产品是否存在
}
// OPTIONS - 获取支持的方法
@RequestMapping(method = RequestMethod.OPTIONS)
public ResponseEntity<Void> options() {
return ResponseEntity.ok()
.allow(HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT,
HttpMethod.DELETE, HttpMethod.PATCH)
.build();
}
}
2. 内容协商
@RestController
@RequestMapping("/api/users")
public class UserController {
// 根据 Accept 头返回不同格式
@GetMapping(value = "/{id}", produces = {
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE
})
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
return ResponseEntity.ok(user);
}
// 根据 Content-Type 头接收不同格式
@PostMapping(consumes = {
MediaType.APPLICATION_JSON_VALUE,
MediaType.APPLICATION_XML_VALUE
})
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = userService.create(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
// 文件上传
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<ApiResponse<String>> uploadFile(
@RequestParam("file") MultipartFile file) {
String fileName = fileService.upload(file);
return ResponseEntity.ok(ApiResponse.success(fileName, "文件上传成功"));
}
}
请求响应处理
1. 请求参数处理
@RestController
@RequestMapping("/api/search")
public class SearchController {
// 查询参数
@GetMapping("/users")
public ResponseEntity<List<User>> searchUsers(
@RequestParam(required = false) String keyword,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "id") String sortBy,
@RequestParam(defaultValue = "asc") String sortDir) {
// 搜索逻辑
}
// 路径参数
@GetMapping("/users/{userId}/posts/{postId}")
public ResponseEntity<Post> getUserPost(
@PathVariable Long userId,
@PathVariable Long postId) {
// 获取用户的特定文章
}
// 请求头参数
@GetMapping("/profile")
public ResponseEntity<User> getProfile(
@RequestHeader("Authorization") String token,
@RequestHeader(value = "User-Agent", required = false) String userAgent) {
// 根据 token 获取用户信息
}
// Cookie 参数
@GetMapping("/preferences")
public ResponseEntity<Map<String, Object>> getPreferences(
@CookieValue(value = "sessionId", required = false) String sessionId) {
// 获取用户偏好设置
}
}
2. 复杂对象绑定
// DTO 类
public class UserSearchRequest {
private String keyword;
private Integer minAge;
private Integer maxAge;
private List<String> roles;
private Boolean active;
private String city;
// getter 和 setter
}
@RestController
public class UserSearchController {
// 复杂查询对象
@PostMapping("/api/users/search")
public ResponseEntity<List<User>> searchUsers(@RequestBody UserSearchRequest request) {
List<User> users = userService.search(request);
return ResponseEntity.ok(users);
}
}
数据验证与转换
1. Bean Validation
// 用户创建请求 DTO
public class CreateUserRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 3, max = 50, message = "用户名长度必须在3-50之间")
@JsonProperty("username")
private String username;
@Email(message = "邮箱格式不正确")
@NotBlank(message = "邮箱不能为空")
@JsonProperty("email")
private String email;
@NotBlank(message = "密码不能为空")
@Size(min = 8, max = 20, message = "密码长度必须在8-20之间")
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d).*$",
message = "密码必须包含大小写字母和数字")
@JsonProperty("password")
private String password;
@Range(min = 18, max = 100, message = "年龄必须在18-100之间")
@JsonProperty("age")
private Integer age;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
@JsonProperty("phone")
private String phone;
// getter 和 setter
}
// 控制器中使用验证
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping
public ResponseEntity<ApiResponse<User>> createUser(
@Valid @RequestBody CreateUserRequest request) {
User user = userService.create(request);
return ResponseEntity.status(HttpStatus.CREATED)
.body(ApiResponse.success(user));
}
@PutMapping("/{id}")
public ResponseEntity<ApiResponse<User>> updateUser(
@PathVariable Long id,
@Valid @RequestBody UpdateUserRequest request) {
User user = userService.update(id, request);
return ResponseEntity.ok(ApiResponse.success(user));
}
}
2. 自定义验证器
// 自定义验证注解
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = UsernameValidator.class)
public @interface ValidUsername {
String message() default "用户名格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
// 验证器实现
public class UsernameValidator implements ConstraintValidator<ValidUsername, String> {
@Override
public boolean isValid(String username, ConstraintValidatorContext context) {
if (username == null || username.trim().isEmpty()) {
return false;
}
// 用户名验证规则:3-20位字母数字下划线,不能以数字开头
String regex = "^[a-zA-Z_][a-zA-Z0-9_]{2,19}$";
return username.matches(regex);
}
}
// 使用自定义验证
public class User {
@ValidUsername
@JsonProperty("username")
private String username;
// ...
}
异常处理机制
1. 全局异常处理器
@ControllerAdvice
@RestController
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 处理参数验证异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ApiResponse<Map<String, String>> handleValidationException(
MethodArgumentNotValidException ex) {
logger.warn("参数验证失败: {}", ex.getMessage());
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ApiResponse.error(400, "参数验证失败", errors);
}
/**
* 处理业务异常
*/
@ExceptionHandler(BusinessException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ApiResponse<Void> handleBusinessException(BusinessException ex) {
logger.warn("业务异常: {}", ex.getMessage());
return ApiResponse.error(ex.getCode(), ex.getMessage());
}
/**
* 处理系统异常
*/
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ApiResponse<Void> handleGenericException(Exception ex) {
logger.error("系统异常", ex);
return ApiResponse.error(500, "系统内部错误,请稍后重试");
}
}
CORS 跨域配置
1. 全局 CORS 配置
@Configuration
public class CorsConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
configuration.setMaxAge(3600L);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", configuration);
return source;
}
}
最佳实践
1. API 设计原则
- RESTful 风格: 使用标准 HTTP 方法和状态码
- 资源导向: URL 应该表示资源而不是动作
- 版本控制: 为 API 提供版本控制策略
- 一致性: 保持命名和响应格式的一致性
2. 安全最佳实践
// 输入验证示例
@RestController
public class SecurityController {
@PostMapping("/api/search")
public ResponseEntity<List<User>> search(@RequestBody SearchRequest request) {
// 输入长度限制
if (request.getKeyword().length() > 100) {
throw new IllegalArgumentException("搜索关键字过长");
}
// 特殊字符过滤
String sanitizedKeyword = request.getKeyword()
.replaceAll("[<>\"'%;()&+]", "");
// 业务逻辑
List<User> users = searchService.search(sanitizedKeyword);
return ResponseEntity.ok(users);
}
}
常见问题
问题1:JSON 数据绑定失败
现象: @RequestBody 注解的参数接收不到数据 原因:
- 缺少 Jackson 依赖
- Content-Type 不正确
- JSON 格式错误
解决方案:
<!-- 确保包含 Jackson 依赖 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
问题2:CORS 跨域问题
现象: 浏览器控制台出现跨域错误 解决方案:
@Configuration
public class CorsConfig {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("*"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
⚠️ 注意: 在生产环境中,务必配置适当的安全策略、监控和日志记录。
相关文章
前后章节导航
- 上一章:第十三章:数据绑定
- 下一章:第十六章:文件上传和下载
Spring 核心技术
MyBatis 与数据访问
相关实操
总结
Spring MVC RESTful API 开发是现代 Java Web 开发的核心技能,本指南全面覆盖了:
🎯 核心内容
- RESTful 架构: 标准的 REST 设计原则和最佳实践
- JSON 处理: 完整的 JSON 序列化和反序列化解决方案
- 数据验证: Bean Validation 和自定义验证器
- 异常处理: 全局异常处理和错误响应统一化
🛠️ 技术特性
- 标准化设计: 遵循 RESTful 设计规范
- 企业级特性: 包含安全、性能、监控等企业级考虑
- 完整工具链: 从开发到测试的完整解决方案
📚 实际应用
- 微服务架构: 服务间通信接口设计
- 前后端分离: Web 应用后端 API 开发
- 移动端支持: iOS/Android 应用后端接口
- 第三方集成: 开放平台 API 设计