Spring Boot 整合 Spring MVC /(整合Web)笔记

1. Spring Boot 整合 Web 功能

Spring Boot 通过自动配置简化了 Spring MVC 的集成。只需在 pom.xml 中添加 spring-boot-starter-web 依赖,Spring Boot 就会自动配置 Spring MVC 的相关组件。

XML 复制代码
<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 数据。

    java 复制代码
    @RestController //定义这个表示这个class是一个Controller
    public class MyController {
        // 方法定义
    }

2.2请求映射注解

**一、@RequestMapping:**用于映射 HTTP 请求到控制器方法。可以指定请求方法、路径等。

1. 作用

@RequestMapping是Spring MVC中用于映射HTTP请求到控制器方法的核心注解。它允许开发者指定请求的路径、方法类型、请求头、参数等条件,从而精确地匹配请求到特定的处理方法。

2. 常用属性
(1)valuepath属性
  • 作用:指定请求的路径。可以是一个字符串或字符串数组,支持路径变量和通配符。

  • 示例

    java 复制代码
    @RequestMapping("/home")
    public String home() {
        return "Home Page";
    }
    
    //或者
    
    @RequestMapping(path = {"/home", "/index"})
    public String home() {
        return "Home Page";
    }
    (2)method属性
  • 作用指定允许的HTTP请求方法(如GETPOSTPUTDELETE等)。如果不指定,默认支持所有HTTP方法。

  • 示例

    java 复制代码
    @RequestMapping(value = "/user", method = RequestMethod.GET)
    public String getUser() {
        return "Get User";
    }
(3)params属性
  • 作用:指定请求必须包含的参数。可以用于限制请求的条件。

  • 示例

    java 复制代码
    @RequestMapping(value = "/user", params = "id")
    public String getUser(@RequestParam String id) {
        return "User ID: " + id;
    }
    3. **@RequestMapping:**使用场景

    @RequestMapping是一个通用的注解,适用于需要同时支持多种HTTP方法的场景。例如:

java 复制代码
@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)

  • 例如

    java 复制代码
    @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:绑定查询参数

java 复制代码
@GetMapping("/user")
public String getUserByName(@RequestParam String name) {
    // 根据名称获取用户
    return "User Name: " + name;
}

@PathVariable:绑定路径参数

java 复制代码
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id) {
    // 根据 ID 获取用户
    return "User ID: " + id;
}

@RequestBody:绑定请求体(JSON/XML)

java 复制代码
@PostMapping("/user")
public String createUser(@RequestBody User user) {
    // 处理用户创建逻辑
    return "User created";
}

@RequestHeader:获取请求头

java 复制代码
@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值

java 复制代码
@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;
}

数组/集合接收:

java 复制代码
@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参数绑定:

java 复制代码
@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()); 
}

多维数组参数:

java 复制代码
// 请求示例:/matrix;categories=手机,电脑;brands=华为,苹果 
@GetMapping("/matrix")
public String processMatrix(
    @MatrixVariable(pathVar = "matrix") 
    Map<String, List<String>> params) {
    return "分类:" + params.get("categories"); 
}

自定义格式转换:

java 复制代码
@GetMapping("/custom")
public void processCustomFormat(
    @RequestParam("codes") 
    @DateTimeFormat(pattern = "yyyyMMdd") List<Date> dateList) {
    // 接收形如?codes=20230201,20230202的参数 
}

@ModelAttribute 模型数据注解:

java 复制代码
@PostMapping("/user")
public String createUser(@ModelAttribute User user) {
    // 处理用户创建逻辑
    return "User created";
}

使用 IO 读取 POST 请求体(可以通过 HttpServletRequest 对象手动读取 POST 请求体。)

java 复制代码
@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.propertiesapplication.yml 文件配置应用的根路径。

java 复制代码
#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 ? 通配符

? 通配符用于匹配单个字符。示例代码:

java 复制代码
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 * 通配符

* 通配符用于匹配零个或多个字符,但不包括路径分隔符 /示例代码

java 复制代码
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 ** 通配符

** 通配符用于匹配零个或多个路径片段,也就是可以跨路径分隔符 / 进行匹配。示例代码

java 复制代码
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正则表达式语法。以下为典型应用场景:示例代码

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 格式)。

java 复制代码
@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
    }
}

响应示例:

java 复制代码
{
  "id": 1,
  "name": "John",
  "age": 30
}
1.2 使用 @ResponseBody

如果使用 @Controller 而不是 @RestController,可以在方法上添加 @ResponseBody 注解,表示返回值直接作为响应体返回。

java 复制代码
@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 注解表示该类是一个控制器,方法返回值会被解析为视图名称。

java 复制代码
@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):

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 是一个包含模型数据和视图名称的对象。

java 复制代码
@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 支持直接返回文件内容。可以通过 ResponseEntityHttpServletResponse 实现。

3.1 使用 ResponseEntity
java 复制代码
@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
java 复制代码
@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: 前缀实现重定向。

java 复制代码
@Controller
public class UserController {

    @PostMapping("/user")
    public String createUser(@ModelAttribute User user) {
        // 处理用户创建逻辑
        return "redirect:/user/" + user.getId(); // 重定向到用户详情页
    }
}

5. 返回自定义响应

可以通过 ResponseEntity 返回自定义的 HTTP 状态码和响应体。

java 复制代码
@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. 返回纯文本

可以直接返回字符串作为纯文本响应。

java 复制代码
@RestController
public class UserController {

    @GetMapping("/hello")
    public String sayHello() {
        return "Hello, World!"; // 返回纯文本
    }
}

7. 返回 XML 数据

如果客户端请求的是 XML 格式,Spring Boot 会自动将对象序列化为 XML。

java 复制代码
@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 数据
    }
}

响应示例:

XML 复制代码
<user>
    <id>1</id>
    <name>John</name>
    <age>30</age>
</user>

8. 返回二进制数据

可以返回字节数组或 Resource 对象作为二进制数据。

java 复制代码
@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 注解或在配置类中配置跨域支持。

java 复制代码
@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 对象。

  • 文件 :通过 ResponseEntityHttpServletResponse 返回文件内容。

  • 重定向 :通过 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,验证依赖已经默认包含。否则,需要手动添加以下依赖

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2. 定义验证规则

在Bean的属性上添加注解来定义验证规则。例如:

java 复制代码
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 会自动返回错误信息。

java 复制代码
@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("用户创建成功");
    }
}

示例:验证路径参数和查询参数

java 复制代码
@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 文件定义错误信息,并支持国际化:

java 复制代码
# messages.properties
NotEmpty.user.name=用户名不能为空
Size.user.name=用户名长度必须在2到20之间

在注解中引用消息:

java 复制代码
@NotBlank(message = "{NotEmpty.user.name}")
@Size(min = 2, max = 20, message = "{Size.user.name}")
private String name;

分组验证

分组验证允许我们根据不同的场景应用不同的验证规则。例如,创建用户和更新用户可能需要不同的验证规则。

定义分组接口:

java 复制代码
public interface CreateGroup {}
public interface UpdateGroup {}

在 Bean 中指定分组

java 复制代码
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 中使用分组

java 复制代码
@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 创建步骤
  1. 在项目中右键点击目标目录(例如 src/test/http)。

  2. 选择 New -> File

  3. 输入文件名,例如 test-api.http

2. 编写 HTTP 请求

.http 文件中,可以编写多个 HTTP 请求,每个请求以 ### 分隔。以下是一个示例:

java 复制代码
### 获取用户列表
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 后,可能会看到如下响应:

java 复制代码
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 和耗时:

java 复制代码
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 注解**,以扫描并注册过滤器:

java 复制代码
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 创建过滤器类

java 复制代码
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 注册过滤器:

java 复制代码
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. 过滤器的执行顺序

如果有多个过滤器,可以通过 FilterRegistrationBeansetOrder 方法设置执行顺序。值越小,优先级越高。

4.1 示例:多个过滤器的执行顺序

java 复制代码
@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 字符编码设置

统一设置请求和响应的字符编码,避免乱码问题。

java 复制代码
@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 跨域处理

在过滤器中设置响应头,支持跨域请求。

java 复制代码
@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 注解设置过滤器的执行顺序。

java 复制代码
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 提供的一个抽象类,用于确保每个请求只被过滤器处理一次。它解决了在过滤器链中可能因为请求被多次处理而导致的问题,例如在请求被转发或包含时,过滤器逻辑被重复执行。

java 复制代码
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

java 复制代码
@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 常用注解

  1. 控制器注解@RestController@Controller@ResponseBody 的组合,用于创建返回 JSON 或其他内容的 RESTful 风格控制器。
  2. 请求映射注解
    • @RequestMapping :可指定请求方法、路径、参数等,valuepath指定路径,method指定请求方法,params根据请求参数匹配。
    • 快捷请求映射注解@GetMapping@PostMapping@PutMapping@DeleteMapping 分别对应 GET、POST、PUT、DELETE 请求,使用更简洁。
  3. 参数绑定注解
    • @RequestParam:绑定查询参数。
    • @PathVariable:绑定路径参数。
    • @RequestBody:绑定请求体(如 JSON/XML)。
    • @RequestHeader:获取请求头。
    • @CookieValue:获取 Cookie 值。
    • 数组 / 集合接收:可接收多个同名参数或 JSON 数组。
    • JSON 参数绑定:自动将 JSON 转换为对象。
    • 多维数组参数 :使用@MatrixVariable处理矩阵变量。
    • 自定义格式转换 :通过@DateTimeFormat等注解实现。
    • @ModelAttribute:用于将请求参数绑定到 Java 对象,并添加到模型中。
  4. 请求路径设置 :在application.propertiesapplication.yml中通过server.servlet.context-path配置应用根路径。
  5. 路径通配符
    • ?通配符:匹配单个字符。
    • *通配符 :匹配零个或多个字符,但不跨路径分隔符/
    • **通配符 :匹配零个或多个路径片段,可跨路径分隔符/
  6. 路径正则表达式 :通过{变量名:正则表达式}语法对路径变量进行正则约束,如{id:\d+}表示路径变量必须是数字。

三、Spring Boot 返回内容

  1. 返回 JSON 数据 :使用@RestController@Controller + @ResponseBody,Spring Boot 默认用 Jackson 将 Java 对象序列化为 JSON。
  2. 返回视图 :使用@Controller,返回字符串作为视图名,结合视图技术(如 Thymeleaf)渲染视图,也可使用ModelAndView
  3. 返回文件 :通过ResponseEntityHttpServletResponse返回文件内容。
  4. 返回重定向 :返回redirect:前缀实现重定向。
  5. 返回自定义响应 :使用ResponseEntity返回自定义 HTTP 状态码和响应体。
  6. 返回纯文本:直接返回字符串作为纯文本响应。
  7. 返回 XML 数据:请求为 XML 格式时,Spring Boot 自动将对象序列化为 XML。
  8. 返回二进制数据 :返回字节数组或Resource对象作为二进制数据。
  9. 跨域支持 :通过@CrossOrigin注解或在配置类中配置跨域规则。

四、数据验证与异常处理

  1. 验证注解@NotNull@Size@Min@Max@Email@Valid@Validated等。
  2. 添加依赖 :使用spring-boot-starter-validation
  3. 定义验证规则:在 Bean 属性上添加注解定义规则。
  4. 在 Controller 中使用验证 :通过@Valid@Validated触发验证,用BindingResult处理错误。
  5. 国际化消息 :通过messages.properties文件定义错误信息并支持国际化。
  6. 分组验证:定义分组接口,在 Bean 和 Controller 中指定分组。

五、测试接口

在 IntelliJ IDEA 中,可通过创建.http文件编写 HTTP 请求来测试 RESTful 接口,方便快捷。

六、Spring Boot 结合过滤器

  1. 过滤器概念:在请求到达 Controller 之前或响应返回客户端之前处理请求和响应,用于日志记录、权限验证等。
  2. 实现过滤器 :实现javax.servlet.Filter接口,使用@WebFilter注解并在启动类添加@ServletComponentScan,或通过FilterRegistrationBean注册。
  3. 执行顺序 :通过FilterRegistrationBeansetOrder方法或@Order注解设置,值越小优先级越高。
  4. 应用场景:日志记录、权限验证、字符编码设置、跨域处理等。
  5. 高级用法 :使用OncePerRequestFilter确保每个请求只被过滤器处理一次。
相关推荐
qq_25005686825 分钟前
springboot接入企业微信群机器人消息推送
spring boot·机器人·企业微信
_院长大人_2 小时前
SpringBoot + 百度内容安全实战:自定义注解 + AOP 实现统一内容审核(支持文本 / 图片 / 视频 + 白名单 + 动态开关)
spring boot·安全·音视频
yzq-38413 小时前
Websocket两台服务器之间的通信
spring boot·websocket·网络协议
rengang663 小时前
002-Spring AI Alibaba Prompt 功能完整案例
人工智能·spring·prompt·spring ai·ai应用编程
星星落进兜里3 小时前
Spring全家桶面试题, 只补充细节版本
java·后端·spring
摇滚侠4 小时前
Spring Boot3零基础教程,SpringSecurity 测试,笔记81
spring boot·笔记·后端
摇滚侠5 小时前
Spring Boot3零基础教程,定制 Health 健康端点,笔记83
spring boot·笔记·spring
芙蓉王真的好15 小时前
优化 Jenkins 构建脚本:避免 pnpm lockfile 相关报错的关键配置
spring·servlet·jenkins
ysdysyn6 小时前
Java奇幻漂流:从Spring秘境到微服务星辰的冒险指南
java·spring·微服务
一 乐8 小时前
商城推荐系统|基于SprinBoot+vue的商城推荐系统(源码+数据库+文档)
前端·数据库·vue.js·spring boot·后端·商城推荐系统