1. Spring Boot 整合 Web 功能
Spring Boot 通过自动配置简化了 Spring MVC 的集成。只需在 pom.xml
中添加 spring-boot-starter-web
依赖,Spring Boot 就会自动配置 Spring MVC 的相关组件。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. Spring MVC 常用注解
2.1 控制器注解
-
@Controller
: 标识一个类为 Spring MVC 控制器。 -
@RestController
: 组合了@Controller
和@ResponseBody
,用于返回 JSON或 XML 数据。@RestController //定义这个表示这个class是一个Controller public class MyController { // 方法定义 }
2.2请求映射注解
-
一、@RequestMapping
: 用于映射 HTTP 请求到控制器方法。可以指定请求方法、路径等。 -
1. 作用
@RequestMapping
是Spring MVC中用于映射HTTP请求到控制器方法的核心注解。它允许开发者指定请求的路径、方法类型、请求头、参数等条件,从而精确地匹配请求到特定的处理方法。2. 常用属性
(1)
value
或path
属性 -
作用:指定请求的路径。可以是一个字符串或字符串数组,支持路径变量和通配符。
-
示例:
@RequestMapping("/home") public String home() { return "Home Page"; } //或者 @RequestMapping(path = {"/home", "/index"}) public String home() { return "Home Page"; }
(2)
method
属性 -
作用:指定允许的HTTP请求方法(如
GET
、POST
、PUT
、DELETE
等)。如果不指定,默认支持所有HTTP方法。 -
示例:
@RequestMapping(value = "/user", method = RequestMethod.GET) public String getUser() { return "Get User"; }
-
(3)
params
属性 -
作用:指定请求必须包含的参数。可以用于限制请求的条件。
-
示例:
@RequestMapping(value = "/user", params = "id") public String getUser(@RequestParam String id) { return "User ID: " + id; }
3.
@RequestMapping
:使用场景@RequestMapping
是一个通用的注解,适用于需要同时支持多种HTTP方法的场景。例如: -
@RequestMapping("/user") public String user(@RequestParam String action) { if ("create".equals(action)) { return "Create User"; } else if ("delete".equals(action)) { return "Delete User"; } return "Unknown Action"; }
二、
@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
这些注解是
@RequestMapping
的衍生注解,专门用于处理特定的HTTP方法。它们是@RequestMapping
的简化形式,使代码更清晰、更易读。 -
@GetMapping
: 专门用于处理 GET 请求。 等价于:@RequestMapping(method = RequestMethod.GET) -
@PostMapping
: 专门用于处理 POST 请求。等价于:@RequestMapping(method = RequestMethod.POST) -
@PutMapping
: 专门用于处理 PUT 请求。等价于:@RequestMapping(method = RequestMethod.PUT) -
@DeleteMapping
: 专门用于处理 DELETE 请求。等价于:@RequestMapping(method = RequestMethod.DELETE) -
例如
@RestController @RequestMapping("/api/user") public class UserController { @GetMapping("/{id}") public User getUser(@PathVariable Long id) { return new User(id, "John Doe"); } @PostMapping public User createUser(@RequestBody User user) { return user; } @PutMapping("/{id}") public User updateUser(@PathVariable Long id, @RequestBody User user) { user.setId(id); return user; } @DeleteMapping("/{id}") public String deleteUser(@PathVariable Long id) { return "User with ID " + id + " deleted"; } }
-
2.3 参数绑定注解
-
@RequestParam
:绑定查询参数@GetMapping("/user") public String getUserByName(@RequestParam String name) { // 根据名称获取用户 return "User Name: " + name; }
-
@PathVariable
:绑定路径参数@GetMapping("/user/{id}") public String getUser(@PathVariable Long id) { // 根据 ID 获取用户 return "User ID: " + id; }
-
@RequestBody
:绑定请求体(JSON/XML)@PostMapping("/user") public String createUser(@RequestBody User user) { // 处理用户创建逻辑 return "User created"; }
-
@RequestHeader
:获取请求头@GetMapping("/headers") public Map<String, String> getAllHeaders( @RequestHeader Map<String, String> headers) { return headers.entrySet().stream() .filter(entry -> !entry.getKey().startsWith("sec-")) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); }
-
@CookieValue
:获取Cookie值@GetMapping("/cookie") public String readCookie( @CookieValue(name = "SESSION_ID", defaultValue = "guest") String sessionId, @CookieValue(value = "USER_THEME", required = false) String theme) { String response = "当前会话ID:" + sessionId; if (theme != null) { response += "<br>主题偏好:" + theme; } return response; }
-
-
数组/集合接收:
@PostMapping("/batchDelete") public void batchDelete(@RequestParam("ids") List<Long> idList) { // 接收ids=1,2,3 或 ids=1&ids=2 } // 示例请求:/batch/1001,1002,1003 @GetMapping("/batch/{ids}") public String processBatchIds( @PathVariable("ids") List<String> idList) { return "接收ID数量:" + idList.size(); }
JSON参数绑定:
-
@PostMapping("/createUser") public User createUser(@RequestBody @Valid UserDTO userDTO) { // 自动将JSON转换为UserDTO对象 } // 请求体示例:[{"name":"手机"},{"name":"电脑"}] @PostMapping("/products") public void createProducts( @RequestBody List<ProductDTO> productList) { productService.batchCreate(productList); } public class OrderRequest { private List<OrderItem> items; // getter/setter } @PostMapping("/orders") public void createOrder( @RequestBody OrderRequest request) { processItems(request.getItems()); }
多维数组参数:
// 请求示例:/matrix;categories=手机,电脑;brands=华为,苹果 @GetMapping("/matrix") public String processMatrix( @MatrixVariable(pathVar = "matrix") Map<String, List<String>> params) { return "分类:" + params.get("categories"); }
自定义格式转换:
@GetMapping("/custom") public void processCustomFormat( @RequestParam("codes") @DateTimeFormat(pattern = "yyyyMMdd") List<Date> dateList) { // 接收形如?codes=20230201,20230202的参数 }
@ModelAttribute 模型数据注解:
@PostMapping("/user") public String createUser(@ModelAttribute User user) { // 处理用户创建逻辑 return "User created"; }
使用 IO 读取 POST 请求体(可以通过
HttpServletRequest
对象手动读取 POST 请求体。)@PostMapping("/upload") public String upload(HttpServletRequest request) throws IOException { InputStream inputStream = request.getInputStream(); // 处理输入流 return "Upload successful"; } //读取字符串 @PostMapping("/param/json2") public String p6(Reader reader) { StringBuilder content = new StringBuilder(""); try (BufferedReader bin = new BufferedReader(reader)) { var line = ""; while ((line = bin.readLine()) != null) { content.append(line); } } catch (IOException e) { e.printStackTrace(); } return "p7, reader=" + content.toString(); } //读取字符串 @PostMapping("/upload") public String upload(HttpServletRequest request) throws IOException { StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { stringBuilder.append(line); } } String requestBody = stringBuilder.toString(); // 获取请求体内容 System.out.println("Request Body: " + requestBody); return "Upload successful"; } //读取二进制数据 @PostMapping("/upload") public String upload(HttpServletRequest request) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] data = new byte[1024]; int bytesRead; try (InputStream inputStream = request.getInputStream()) { while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) { buffer.write(data, 0, bytesRead); } } byte[] requestBody = buffer.toByteArray(); // 获取请求体的字节数组 System.out.println("Request Body Size: " + requestBody.length); return "Upload successful"; }
2.4 请求路径设置
Spring Boot 中可以通过 application.properties
或 application.yml
文件配置应用的根路径。
#application.properties
server.servlet.context-path=/myapp
#application.yml
server:
servlet:
context-path: /myapp
2.5、路径通配符
在 Spring Boot 中,使用 Spring MVC 处理 Web 请求时,确实可以通过多种方式设置请求的路径,包括使用通配符和正则表达式,下面为你详细介绍相关内容。
25.1. 通配符的使用
Spring MVC 支持两种通配符:?
和 *
,另外还有一种更强大的 **
通配符。
2.5.1.1 ?
通配符
?
通配符用于匹配单个字符。示例代码:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class WildcardController {
@GetMapping("/user?")
public String handleUserPath() {
return "This path matches URLs like /user1, /userA etc.";
}
}
//在上述代码中,/user? 可以匹配 /user1、/userA 等路径,
//但不能匹配 /user 或 /user12 等路径,因为 ? 只能匹配单个字符。
在上述代码中,/user? 可以匹配 /user1、/userA 等路径,但不能匹配 /user 或 /user12 等路径,因为 ? 只能匹配单个字符。
2.5.1.2 *
通配符
*
通配符用于匹配零个或多个字符,但不包括路径分隔符 /
。示例代码:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SingleAsteriskController {
@GetMapping("/user/*")
public String handleUserPathWithSingleAsterisk() {
return "This path matches URLs like /user/profile, /user/info etc.";
}
}
//这里 /user/* 可以匹配 /user/profile、/user/info 等路径,
//但不能匹配 /user/profile/subprofile,
//因为 * 不会跨路径分隔符 / 进行匹配。
这里 /user/*
可以匹配 /user/profile
、/user/info
等路径,但不能匹配 /user/profile/subprofile
,因为 *
不会跨路径分隔符 /
进行匹配。
2.51.3 **
通配符
**
通配符用于匹配零个或多个路径片段,也就是可以跨路径分隔符 /
进行匹配。示例代码:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DoubleAsteriskController {
@GetMapping("/user/**")
public String handleUserPathWithDoubleAsterisk() {
return "This path matches URLs like /user/profile, /user/profile/subprofile etc.";
}
}
// /user/** 可以匹配 /user/profile、/user/profile/subprofile
//等任意以 /user/ 开头的路径。
/user/**
可以匹配 /user/profile
、/user/profile/subprofile
等任意以 /user/
开头的路径。
2.6. 路径正则表达式的使用
在Spring MVC中,路径变量的正则约束通过`{变量名:正则表达式}`语法实现,支持标准Java正则表达式语法。以下为典型应用场景:示例代码:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RegexController {
@GetMapping("/users/{id:\\d+}")
public String getUserById(@PathVariable("id") String id) {
return "User ID: " + id;
}
}
//在上述代码中,{id:\\d+} 表示 id 这个路径变量必须是一个或多个数字。
//因此,像 /users/123 这样的请求可以匹配该方法,但 /users/abc 则无法匹配。
// UUID格式验证
@GetMapping("/documents/{uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}")
public String getDocument(@PathVariable String uuid) {
return "Document UUID: " + uuid;
}
// 十六进制颜色代码
@GetMapping("/colors/{hex:#[0-9a-fA-F]{6}}")
public String getColor(@PathVariable String hex) {
return "Color code: " + hex;
}
// 日期+序列号格式:20230224-ABC123
@GetMapping("/orders/{code:\\d{8}-[A-Z]{3}\\d{3}}")
public String getOrder(@PathVariable String code) {
return "Order Code: " + code;
}
// 排除特殊字符的路径参数
@GetMapping("/search/{query:[^/?*:;{}]+}")
public String search(@PathVariable String query) {
return "Searching: " + query;
}
// 长度+字符类型双重约束
@GetMapping("/products/{sku:[A-Z]{2}-\\d{4}-[a-z]{3}}")
public String getProduct(
@PathVariable String sku) {
return "Product SKU: " + sku;
}
在上述代码中,{id:\\d+}
表示 id
这个路径变量必须是一个或多个数字。因此,像 /users/123
这样的请求可以匹配该方法,但 /users/abc
则无法匹配。
2.7、Spring Boot 返回内容详解
在 Spring Boot 中,控制器方法的返回值决定了客户端接收到的响应内容。Spring Boot 支持多种返回类型,包括 JSON、视图、文件、重定向等。以下是对 Spring Boot 返回内容的详细说明。
1. 返回 JSON 数据
Spring Boot 默认使用 Jackson 库将 Java 对象序列化为 JSON 数据。如果控制器方法返回一个对象,Spring Boot 会自动将其转换为 JSON 格式并返回。
1.1 使用 @RestController
@RestController
是 @Controller
和 @ResponseBody
的组合注解,表示该控制器中的所有方法返回值都会直接作为响应体返回(通常是 JSON 格式)。
@RestController
public class UserController {
@GetMapping("/user/{id}")
public User getUser(@PathVariable Long id) {
User user = new User();
user.setId(id);
user.setName("John");
user.setAge(30);
return user; // 自动转换为 JSON
}
}
响应示例:
{
"id": 1,
"name": "John",
"age": 30
}
1.2 使用 @ResponseBody
如果使用 @Controller
而不是 @RestController
,可以在方法上添加 @ResponseBody
注解,表示返回值直接作为响应体返回。
@Controller
public class UserController {
@GetMapping("/user/{id}")
@ResponseBody
public User getUser(@PathVariable Long id) {
User user = new User();
user.setId(id);
user.setName("John");
user.setAge(30);
return user; // 自动转换为 JSON
}
}
2. 返回视图
Spring Boot 支持多种视图技术(如 Thymeleaf、JSP、Freemarker 等)。如果控制器方法返回一个字符串,Spring Boot 会将其解析为视图名称,并渲染对应的视图文件。
2.1 使用 @Controller
@Controller
注解表示该类是一个控制器,方法返回值会被解析为视图名称。
@Controller
public class UserController {
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id, Model model) {
User user = new User();
user.setId(id);
user.setName("John");
user.setAge(30);
model.addAttribute("user", user); // 将数据传递给视图
return "userDetail"; // 返回视图名称
}
}
视图文件(userDetail.html
):
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>User Detail</title>
</head>
<body>
<h1>User Information</h1>
<p>ID: <span th:text="${user.id}"></span></p>
<p>Name: <span th:text="${user.name}"></span></p>
<p>Age: <span th:text="${user.age}"></span></p>
</body>
</html>
2.2 使用 ModelAndView
ModelAndView
是一个包含模型数据和视图名称的对象。
@Controller
public class UserController {
@GetMapping("/user/{id}")
public ModelAndView getUser(@PathVariable Long id) {
User user = new User();
user.setId(id);
user.setName("John");
user.setAge(30);
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("user", user); // 添加模型数据
modelAndView.setViewName("userDetail"); // 设置视图名称
return modelAndView;
}
}
3. 返回文件
Spring Boot 支持直接返回文件内容。可以通过 ResponseEntity
或 HttpServletResponse
实现。
3.1 使用 ResponseEntity
@RestController
public class FileController {
@GetMapping("/download")
public ResponseEntity<Resource> downloadFile() throws IOException {
File file = new File("path/to/file.txt");
InputStreamResource resource = new InputStreamResource(new FileInputStream(file));
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"")
.contentLength(file.length())
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
}
}
3.2 使用 HttpServletResponse
@RestController
public class FileController {
@GetMapping("/download")
public void downloadFile(HttpServletResponse response) throws IOException {
File file = new File("path/to/file.txt");
response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getName() + "\"");
Files.copy(file.toPath(), response.getOutputStream());
}
}
4. 返回重定向
Spring Boot 支持通过返回 redirect:
前缀实现重定向。
@Controller
public class UserController {
@PostMapping("/user")
public String createUser(@ModelAttribute User user) {
// 处理用户创建逻辑
return "redirect:/user/" + user.getId(); // 重定向到用户详情页
}
}
5. 返回自定义响应
可以通过 ResponseEntity
返回自定义的 HTTP 状态码和响应体。
@RestController
public class UserController {
@GetMapping("/user/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = new User();
user.setId(id);
user.setName("John");
user.setAge(30);
if (user == null) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null); // 返回 404
}
return ResponseEntity.ok(user); // 返回 200 和用户数据
}
}
6. 返回纯文本
可以直接返回字符串作为纯文本响应。
@RestController
public class UserController {
@GetMapping("/hello")
public String sayHello() {
return "Hello, World!"; // 返回纯文本
}
}
7. 返回 XML 数据
如果客户端请求的是 XML 格式,Spring Boot 会自动将对象序列化为 XML。
@RestController
public class UserController {
@GetMapping(value = "/user/{id}", produces = MediaType.APPLICATION_XML_VALUE)
public User getUser(@PathVariable Long id) {
User user = new User();
user.setId(id);
user.setName("John");
user.setAge(30);
return user; // 返回 XML 数据
}
}
响应示例:
<user>
<id>1</id>
<name>John</name>
<age>30</age>
</user>
8. 返回二进制数据
可以返回字节数组或 Resource
对象作为二进制数据。
@RestController
public class ImageController {
@GetMapping(value = "/image", produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] getImage() throws IOException {
File file = new File("path/to/image.jpg");
return Files.readAllBytes(file.toPath());
}
}
9. 跨域支持
可以通过 @CrossOrigin
注解或在配置类中配置跨域支持。
@RestController
@CrossOrigin(origins = "http://example.com") //允许跨域请求。origins属性指定了允许发起请求的源(域)。这里只允许http://example.com域的请求访问该控制器的方法。
//如果需要允许所有域,可以设置为origins = "*",但出于安全考虑,建议限制为特定域。
public class MyController {
// 方法定义
}
//或者在配置类中全局配置:
// 使用 @Configuration 注解标记该类为 Spring 配置类
// Spring Boot 在启动时会加载该配置类
@Configuration
public class CorsConfig implements WebMvcConfigurer {
// 重写 WebMvcConfigurer 接口中的 addCorsMappings 方法
// 该方法用于配置 CORS(跨域资源共享)规则
@Override
public void addCorsMappings(CorsRegistry registry) {
// 调用 registry.addMapping 方法,指定哪些路径启用 CORS 配置
// "/**" 表示对所有路径启用 CORS
registry.addMapping("/**")
// 设置允许跨域请求的源(域名)
// 这里只允许来自 "http://example.com" 的请求跨域访问
.allowedOrigins("http://example.com")
// 设置允许的 HTTP 方法
// 这里允许 GET、POST、PUT 和 DELETE 方法
.allowedMethods("GET", "POST", "PUT", "DELETE");
// 还可以继续链式调用其他方法,例如:
// .allowedHeaders("*") // 允许所有请求头
// .allowCredentials(true) // 允许携带凭证(如 Cookie)
// .maxAge(3600); // 预检请求的缓存时间(单位:秒)
}
}
10. 总结
Spring Boot 支持多种返回类型,包括:
-
JSON 数据:通过
@RestController
或@ResponseBody
返回。 -
视图:通过
@Controller
返回视图名称或ModelAndView
对象。 -
文件:通过
ResponseEntity
或HttpServletResponse
返回文件内容。 -
重定向:通过
redirect:
前缀实现重定向。 -
自定义响应:通过
ResponseEntity
返回自定义状态码和响应体。 -
纯文本:直接返回字符串。
-
XML 数据:通过设置
produces
属性返回 XML 格式数据。 -
二进制数据:返回字节数组或
Resource
对象。
根据业务需求选择合适的返回类型,可以极大地简化开发工作并提高代码的可读性。
2.8、数据验证与异常处理
在Spring Boot中,Java Bean Validation是一种强大的工具,用于验证域模型的属性值是否符合预期。它通过将验证规则集中到Bean对象中,实现了在一处统一控制所有验证逻辑,从而简化了Controller和Service层的代码。
注解 | 描述 |
---|---|
@NotNull | 验证元素值不为null |
@Size | 验证字符串、集合等的大小是否在指定范围内 |
@Min 和 @Max | 验证数值是否在指定范围内 |
@Email | 验证字符串是否为合法的电子邮件地址 |
@Valid | 用于方法参数或返回值,启用级联验证 |
@Validated | 与@Valid 类似,但支持分组验证 |
1. 添加依赖
如果使用Spring Boot Starter Web,验证依赖已经默认包含。否则,需要手动添加以下依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2. 定义验证规则
在Bean的属性上添加注解来定义验证规则。例如:
public class User {
@NotNull(message = "用户ID不能为空") //可以在注解中直接定义错误信息:
private Long id;
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度必须在2到20之间")
private String name;
@Min(value = 18, message = "年龄必须大于或等于18")
@Max(value = 100, message = "年龄必须小于或等于100")
private int age;
@Email(message = "邮箱格式不正确")
private String email;
// Getters and Setters
}
3. 在 Controller 中使用验证
在 Controller 中,可以通过 @Valid
注解触发 Bean 的验证逻辑。如果验证失败,Spring Boot 会自动返回错误信息。
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public ResponseEntity<?> createUser(@Valid @RequestBody User user, BindingResult result) {
// 如果验证失败,收集错误信息到Map中
if (result.hasErrors()) {
Map<String, List<String>> errors = new HashMap<>();
for (FieldError error : result.getFieldErrors()) {
// 如果字段已经存在于Map中,追加错误信息;否则创建新的列表
errors.computeIfAbsent(error.getField(), k -> new ArrayList<>()).add(error.getDefaultMessage());
}
return ResponseEntity.badRequest().body(errors);
}
// 验证通过,处理业务逻辑
return ResponseEntity.ok("用户创建成功");
}
}
示例:验证路径参数和查询参数
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public ResponseEntity<String> getUser(
@PathVariable @Min(value = 1, message = "用户ID必须大于0") Long id,
@RequestParam @NotBlank(message = "用户名不能为空") String name) {
// 处理业务逻辑
return ResponseEntity.ok("用户ID: " + id + ", 用户名: " + name);
}
}
使用国际化消息
可以通过 messages.properties
文件定义错误信息,并支持国际化:
# messages.properties
NotEmpty.user.name=用户名不能为空
Size.user.name=用户名长度必须在2到20之间
在注解中引用消息:
@NotBlank(message = "{NotEmpty.user.name}")
@Size(min = 2, max = 20, message = "{Size.user.name}")
private String name;
分组验证
分组验证允许我们根据不同的场景应用不同的验证规则。例如,创建用户和更新用户可能需要不同的验证规则。
定义分组接口:
public interface CreateGroup {}
public interface UpdateGroup {}
在 Bean 中指定分组
public class User {
@NotNull(message = "用户ID不能为空", groups = UpdateGroup.class)
private Long id;
@NotBlank(message = "用户名不能为空", groups = {CreateGroup.class, UpdateGroup.class})
@Size(min = 2, max = 20, message = "用户名长度必须在2到20之间", groups = {CreateGroup.class, UpdateGroup.class})
private String name;
// Getters and Setters
}
在 Controller 中使用分组
@PostMapping("/create")
public ResponseEntity<String> createUser(@Validated(CreateGroup.class) @RequestBody User user) {
// 处理业务逻辑
return ResponseEntity.ok("用户创建成功");
}
@PostMapping("/update")
public ResponseEntity<String> updateUser(@Validated(UpdateGroup.class) @RequestBody User user) {
// 处理业务逻辑
return ResponseEntity.ok("用户更新成功");
}
在 IntelliJ IDEA 中使用 HTTP 文件测试接口
IntelliJ IDEA 提供了一个非常方便的功能,可以通过编写 .http
文件来测试 RESTful 接口。这种方式非常适合在开发过程中快速验证接口的正确性,而无需依赖 Postman 或其他工具。本文将详细介绍如何在 IDEA 中使用 HTTP 文件测试接口。
1. 创建 HTTP 文件
在 IntelliJ IDEA 中,可以直接创建一个 .http
文件来编写 HTTP 请求。
1.1 创建步骤
-
在项目中右键点击目标目录(例如
src/test/http
)。 -
选择
New -> File
。 -
输入文件名,例如
test-api.http
。
2. 编写 HTTP 请求
在 .http
文件中,可以编写多个 HTTP 请求,每个请求以 ###
分隔。以下是一个示例:
### 获取用户列表
GET http://localhost:8080/api/users
Accept: application/json
### 创建用户
POST http://localhost:8080/api/users
Content-Type: application/json
{
"name": "John Doe",
"age": 30,
"email": "john.doe@example.com"
}
### 获取单个用户
GET http://localhost:8080/api/users/1
Accept: application/json
### 更新用户
PUT http://localhost:8080/api/users/1
Content-Type: application/json
{
"name": "Jane Doe",
"age": 25,
"email": "jane.doe@example.com"
}
### 删除用户
DELETE http://localhost:8080/api/users/1
3. 运行 HTTP 请求
在 .http
文件中,每个请求旁边都会有一个绿色的运行按钮。点击按钮即可发送请求并查看响应。
3.1 查看响应
发送请求后,IDEA 会在右侧的 Run
工具窗口中显示响应结果,包括状态码、响应头和响应体。
例如,运行 GET http://localhost:8080/api/users
后,可能会看到如下响应:
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": 1,
"name": "John Doe",
"age": 30,
"email": "john.doe@example.com"
},
{
"id": 2,
"name": "Jane Doe",
"age": 25,
"email": "jane.doe@example.com"
}
]
3.Spring Boot 结合过滤器的用法
在 Spring Boot 中,过滤器(Filter)是 Java Servlet 规范的一部分,用于在请求到达 Controller 之前或响应返回客户端之前对请求和响应进行处理。过滤器通常用于实现一些通用的功能,例如日志记录、权限验证、字符编码设置等。本文将详细介绍如何在 Spring Boot 中使用过滤器。
1. 什么是过滤器?
过滤器是 Java Web 应用中的一种组件,它可以对 HTTP 请求和响应进行预处理和后处理。过滤器的主要特点包括:
-
链式调用:多个过滤器可以形成一个过滤器链,按顺序依次处理请求和响应。
-
灵活配置:可以指定过滤器拦截的 URL 路径。
-
生命周期:过滤器的生命周期由 Servlet 容器管理,包括初始化、执行和销毁。
2. 实现一个简单的过滤器
在 Spring Boot 中,可以通过实现 javax.servlet.Filter
接口来创建自定义过滤器。
2.1 创建过滤器类
以下是一个简单的过滤器示例,用于记录请求的 URL 和耗时:
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
// 使用 @WebFilter 注解标记该类为过滤器
// urlPatterns 指定过滤器拦截的 URL 路径 这里的/*表示拦截所有的路径
@WebFilter(urlPatterns = "/*")
public class RequestLogFilter implements Filter {
//init() 方法:在过滤器初始化时被调用,通常用于加载资源或初始化配置。
//FilterConfig 参数:提供了过滤器的初始化参数,可以通过它获取在 web.xml 或注解中配置的参数。
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// 过滤器初始化时调用
System.out.println("RequestLogFilter initialized");
}
//doFilter() 方法:是过滤器的核心逻辑,每次请求都会调用该方法。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
// 记录请求开始时间
long startTime = System.currentTimeMillis();
System.out.println("Request URL: " + request.getServletContext().getContextPath());
// 继续执行过滤器链
chain.doFilter(request, response);
// 记录请求耗时
long endTime = System.currentTimeMillis();
System.out.println("Request processed in " + (endTime - startTime) + " ms");
}
//destroy() 方法:在过滤器销毁时被调用,通常用于清理资源。
@Override
public void destroy() {
// 过滤器销毁时调用
System.out.println("RequestLogFilter destroyed");
}
}
2.2 启用过滤器
如果使用 @WebFilter
注解,需要在 Spring Boot 启动类上添加 @ServletComponentScan
注解,以扫描并注册过滤器:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
@SpringBootApplication
@ServletComponentScan // 扫描并注册 @WebFilter、@WebServlet 等注解
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
3. 使用 FilterRegistrationBean
注册过滤器
除了使用 @WebFilter
注解,还可以通过 FilterRegistrationBean
手动注册过滤器。这种方式更加灵活,可以动态配置过滤器的顺序和 URL 映射。
3.1 创建过滤器类
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class AuthFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("AuthFilter initialized");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("AuthFilter: Checking authentication");
// 模拟权限验证
String token = request.getParameter("token");
if ("valid-token".equals(token)) {
chain.doFilter(request, response); // 验证通过,继续执行过滤器链
} else {
response.getWriter().write("Unauthorized"); // 验证失败,返回未授权信息
}
}
@Override
public void destroy() {
System.out.println("AuthFilter destroyed");
}
}
3.2 注册过滤器
在 Spring Boot 配置类中,使用 FilterRegistrationBean
注册过滤器:
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<AuthFilter> authFilter() {
FilterRegistrationBean<AuthFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new AuthFilter()); // 设置过滤器实例
registrationBean.addUrlPatterns("/secure/*"); // 设置拦截的 URL 路径
registrationBean.setOrder(1); // 设置过滤器执行顺序
return registrationBean;
}
}
4. 过滤器的执行顺序
如果有多个过滤器,可以通过 FilterRegistrationBean
的 setOrder
方法设置执行顺序。值越小,优先级越高。
4.1 示例:多个过滤器的执行顺序
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<RequestLogFilter> requestLogFilter() {
FilterRegistrationBean<RequestLogFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new RequestLogFilter());
registrationBean.addUrlPatterns("/*");
registrationBean.setOrder(2); // 第二个执行 这里的数值越小就越先执行
return registrationBean;
}
@Bean
public FilterRegistrationBean<AuthFilter> authFilter() {
FilterRegistrationBean<AuthFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new AuthFilter());
registrationBean.addUrlPatterns("/secure/*");
registrationBean.setOrder(1); // 第一个执行 这里的数值越小就越先执行
return registrationBean;
}
}
5. 过滤器的常见应用场景
5.1 日志记录
记录请求的 URL、参数、耗时等信息,便于调试和监控。
5.2 权限验证
在请求到达 Controller 之前,验证用户的权限或 Token。
5.3 字符编码设置
统一设置请求和响应的字符编码,避免乱码问题。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
chain.doFilter(request, response);
}
5.4 跨域处理
在过滤器中设置响应头,支持跨域请求。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, Authorization");
chain.doFilter(request, response);
}
6. 过滤器高级用法
6.1 使用 @Order
注解设置过滤器顺序
除了使用 FilterRegistrationBean
,还可以通过 @Order
注解设置过滤器的执行顺序。
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
@Component
@Order(1) // 设置过滤器执行顺序
public class CustomFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("CustomFilter initialized");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
System.out.println("CustomFilter: Before chain.doFilter");
chain.doFilter(request, response);
System.out.println("CustomFilter: After chain.doFilter");
}
@Override
public void destroy() {
System.out.println("CustomFilter destroyed");
}
}
6.2 使用 OncePerRequestFilter
OncePerRequestFilter
是 Spring 提供的一个抽象类,用于确保每个请求只被过滤器处理一次。它解决了在过滤器链中可能因为请求被多次处理而导致的问题,例如在请求被转发或包含时,过滤器逻辑被重复执行。
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class CustomOncePerRequestFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
// 在调用下一个过滤器或目标资源之前执行的逻辑
System.out.println("CustomOncePerRequestFilter: Before doFilter");
// 调用下一个过滤器或目标资源
filterChain.doFilter(request, response);
// 在调用下一个过滤器或目标资源之后执行的逻辑
System.out.println("CustomOncePerRequestFilter: After doFilter");
}
}
注册 OncePerRequestFilter
:
@Bean
public FilterRegistrationBean<CustomOncePerRequestFilter> customOncePerRequestFilter() {
FilterRegistrationBean<CustomOncePerRequestFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new CustomOncePerRequestFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
在 Spring Boot 中使用过滤器,可以实现对请求和响应的统一处理。本文介绍了以下内容:
-
如何通过
@WebFilter
注解创建过滤器。 -
如何使用
FilterRegistrationBean
注册过滤器。 -
如何设置过滤器的执行顺序。
-
过滤器的常见应用场景。
Spring Boot 整合 Web 功能总结:
一、Spring Boot 整合 Web 功能
通过在pom.xml
添加spring-boot-starter-web
依赖,Spring Boot 自动配置 Spring MVC 相关组件,极大简化了 Spring MVC 的集成过程。
二、Spring MVC 常用注解
- 控制器注解:
@RestController
是@Controller
和@ResponseBody
的组合,用于创建返回 JSON 或其他内容的 RESTful 风格控制器。 - 请求映射注解
@RequestMapping
:可指定请求方法、路径、参数等,value
或path
指定路径,method
指定请求方法,params
根据请求参数匹配。- 快捷请求映射注解:
@GetMapping
、@PostMapping
、@PutMapping
、@DeleteMapping
分别对应 GET、POST、PUT、DELETE 请求,使用更简洁。
- 参数绑定注解
@RequestParam
:绑定查询参数。@PathVariable
:绑定路径参数。@RequestBody
:绑定请求体(如 JSON/XML)。@RequestHeader
:获取请求头。@CookieValue
:获取 Cookie 值。- 数组 / 集合接收:可接收多个同名参数或 JSON 数组。
- JSON 参数绑定:自动将 JSON 转换为对象。
- 多维数组参数:使用
@MatrixVariable
处理矩阵变量。 - 自定义格式转换:通过
@DateTimeFormat
等注解实现。 @ModelAttribute
:用于将请求参数绑定到 Java 对象,并添加到模型中。
- 请求路径设置:在
application.properties
或application.yml
中通过server.servlet.context-path
配置应用根路径。 - 路径通配符
?
通配符:匹配单个字符。*
通配符:匹配零个或多个字符,但不跨路径分隔符/
。**
通配符:匹配零个或多个路径片段,可跨路径分隔符/
。
- 路径正则表达式:通过
{变量名:正则表达式}
语法对路径变量进行正则约束,如{id:\d+}
表示路径变量必须是数字。
三、Spring Boot 返回内容
- 返回 JSON 数据:使用
@RestController
或@Controller
+@ResponseBody
,Spring Boot 默认用 Jackson 将 Java 对象序列化为 JSON。 - 返回视图:使用
@Controller
,返回字符串作为视图名,结合视图技术(如 Thymeleaf)渲染视图,也可使用ModelAndView
。 - 返回文件:通过
ResponseEntity
或HttpServletResponse
返回文件内容。 - 返回重定向:返回
redirect:
前缀实现重定向。 - 返回自定义响应:使用
ResponseEntity
返回自定义 HTTP 状态码和响应体。 - 返回纯文本:直接返回字符串作为纯文本响应。
- 返回 XML 数据:请求为 XML 格式时,Spring Boot 自动将对象序列化为 XML。
- 返回二进制数据:返回字节数组或
Resource
对象作为二进制数据。 - 跨域支持:通过
@CrossOrigin
注解或在配置类中配置跨域规则。
四、数据验证与异常处理
- 验证注解:
@NotNull
、@Size
、@Min
、@Max
、@Email
、@Valid
、@Validated
等。 - 添加依赖:使用
spring-boot-starter-validation
。 - 定义验证规则:在 Bean 属性上添加注解定义规则。
- 在 Controller 中使用验证:通过
@Valid
或@Validated
触发验证,用BindingResult
处理错误。 - 国际化消息:通过
messages.properties
文件定义错误信息并支持国际化。 - 分组验证:定义分组接口,在 Bean 和 Controller 中指定分组。
五、测试接口
在 IntelliJ IDEA 中,可通过创建.http
文件编写 HTTP 请求来测试 RESTful 接口,方便快捷。
六、Spring Boot 结合过滤器
- 过滤器概念:在请求到达 Controller 之前或响应返回客户端之前处理请求和响应,用于日志记录、权限验证等。
- 实现过滤器:实现
javax.servlet.Filter
接口,使用@WebFilter
注解并在启动类添加@ServletComponentScan
,或通过FilterRegistrationBean
注册。 - 执行顺序:通过
FilterRegistrationBean
的setOrder
方法或@Order
注解设置,值越小优先级越高。 - 应用场景:日志记录、权限验证、字符编码设置、跨域处理等。
- 高级用法:使用
OncePerRequestFilter
确保每个请求只被过滤器处理一次。