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确保每个请求只被过滤器处理一次。
相关推荐
程序员张329 分钟前
Maven编译和打包插件
java·spring boot·maven
灵犀学长2 小时前
EasyExcel之SheetWriteHandler:解锁Excel写入的高阶玩法
spring boot·excel
zwjapple3 小时前
docker-compose一键部署全栈项目。springboot后端,react前端
前端·spring boot·docker
DuelCode5 小时前
Windows VMWare Centos Docker部署Springboot 应用实现文件上传返回文件http链接
java·spring boot·mysql·nginx·docker·centos·mybatis
优创学社26 小时前
基于springboot的社区生鲜团购系统
java·spring boot·后端
幽络源小助理6 小时前
SpringBoot基于Mysql的商业辅助决策系统设计与实现
java·vue.js·spring boot·后端·mysql·spring
猴哥源码6 小时前
基于Java+springboot 的车险理赔信息管理系统
java·spring boot
Code blocks7 小时前
使用Jenkins完成springboot项目快速更新
java·运维·spring boot·后端·jenkins
wfsm9 小时前
spring事件使用
java·后端·spring
Exclusive_Cat13 小时前
SpringMVC参数接收与数据返回详解
spring·mvc