ToB企服应用市场:ToB评测及商务社交产业平台

标题: spring mvc详细讲解(前后端分离模式) [打印本页]

作者: 盛世宏图    时间: 2024-9-16 13:51
标题: spring mvc详细讲解(前后端分离模式)
在前后端分离模式下,Spring MVC 的作用主要集中在处理后端的业务逻辑和 API 接口,而不再直接管理视图部分。也就是说,Spring MVC 的重点是怎样处理客户端的请求并返回数据(通常以 JSON 或 XML 格式),而视图渲染交给前端框架(如 Vue.js、React 等)来完成。
下面是针对前后端分离模式的 Spring MVC 详细讲解。
1、Spring MVC 工作流程

在前后端分离的模式下,Spring MVC 的工作流程仍旧包含以下步骤,但其核心作用是处理客户端发送的请求并返回数据,而不再渲染视图。
2、核心组件

在前后端分离的模式中,Spring MVC 的核心组件有以下几个:
2.1 Controller(控制器)

控制器主要负责接收前端的请求,并返回数据。通常,控制器中的方法会通过 @RestController 注解来简化开发,它表示这个控制器只会返回数据而不是视图。
示例:
  1. @RestController
  2. @RequestMapping("/api/users")
  3. public class UserController {
  4.     @GetMapping("/{id}")
  5.     public User getUserById(@PathVariable Long id) {
  6.         // 模拟从数据库获取用户
  7.         User user = new User(id, "John", 25);
  8.         return user;  // 返回 JSON 数据
  9.     }
  10.     @PostMapping("/create")
  11.     public User createUser(@RequestBody User user) {
  12.         // 保存用户逻辑
  13.         return user;  // 返回保存后的用户信息
  14.     }
  15. }
复制代码
在这个例子中,UserController 通过 RESTful API 向前端返回用户数据,而不是返回 HTML 页面。
2.2 @RestController 注解

@RestController 是 @Controller 和 @ResponseBody 的组合注解,简化了返回 JSON、XML 等格式的开发流程。每个方法返回的数据会自动序列化为客户端期望的格式。
2.3 RequestMapping 注解

@RequestMapping 或简化的 @GetMapping、@PostMapping 用于界说接口的 URL 路径,并指定 HTTP 方法类型(GET、POST、PUT、DELETE 等)。它们用来路由前端请求到具体的控制器方法。
2.4 @RequestBody 和 @ResponseBody

3、RequestMapping

@RequestMapping 是 Spring MVC 中的核心注解之一,用于将 HTTP 请求映射随处理器方法(通常是控制器类中的方法)。在前后端分离模式下,@RequestMapping 和相关的注解主要用于构建 RESTful API,确保前端能通过 HTTP 请求与后端举行交互。@RequestMapping 可以精确地映射 HTTP 请求的 URL、请求方法、请求参数等,以便后端可以或许根据前端的请求路径和请求类型执行相应的操作。
1. @RequestMapping 注解的基本利用

@RequestMapping 可以用于类和方法上,常见的组合是将它用在控制器类上以指定公共路径,方法上用来指定更具体的路径和请求操作。
类级别的 @RequestMapping

类级别的 @RequestMapping 用于界说公共的 URL 路径前缀,所有在此类中的方法共享这个前缀。
示例:
  1. @RestController
  2. @RequestMapping("/api/users")
  3. public class UserController {
  4.     // 所有路径都会以 "/api/users" 为前缀
  5. }
复制代码
方法级别的 @RequestMapping

方法级别的 @RequestMapping 用于指定具体的请求路径和操作,可以与类级别的路径组合形成完整的 URL。
示例:
  1. @RestController
  2. @RequestMapping("/api/users")
  3. public class UserController {
  4.     @RequestMapping("/list")
  5.     public List<User> getAllUsers() {
  6.         // 处理 "/api/users/list" 请求
  7.         return userService.getAllUsers();
  8.     }
  9.     @RequestMapping("/details/{id}")
  10.     public User getUserById(@PathVariable Long id) {
  11.         // 处理 "/api/users/details/{id}" 请求
  12.         return userService.getUserById(id);
  13.     }
  14. }
复制代码
2. @RequestMapping 的常用属性

@RequestMapping 提供了多个属性用于更加精确地匹配 HTTP 请求:
2.1 value 属性

value 属性指定请求的路径,可以是一个字符串数组,表示答应多个 URL 访问同一个处理器方法。
  1. @RequestMapping(value = {"/list", "/all"})
  2. public List<User> getAllUsers() {
  3.     // "/list" 或 "/all" 都会被映射到这个方法
  4.     return userService.getAllUsers();
  5. }
复制代码
2.2 method 属性

method 属性用于指定支持的 HTTP 请求方法,例如 GET、POST、PUT、DELETE 等。
  1. @RequestMapping(value = "/create", method = RequestMethod.POST)
  2. public User createUser(@RequestBody User user) {
  3.     // 只处理 POST 请求
  4.     return userService.saveUser(user);
  5. }
复制代码
如果你盼望更简洁地界说请求方法,可以利用特定的注解替代 @RequestMapping,如 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping。
2.3 params 属性

params 属性可以用于限制请求参数的存在与否。例如,如果你盼望方法仅在某个特定参数存在时被调用,可以利用 params 属性。
  1. http://example.com/login?username=yourUsername&password=yourPassword
  2. @RequestMapping(value = "/search", params = {"username", "password"})
  3. public List<User> searchUsersByName(@RequestParam String username, @RequestParam String password) {
  4.     // 只有请求中包含 username 参数时,这个方法才会被执行
  5.     return userService.searchByName(username);
  6. }
复制代码
2.4 headers 属性

headers 属性用于根据 HTTP 请求头匹配请求。这个属性可以用来在某些特定的请求头存在时执行某个处理方法。
  1. @RequestMapping(value = "/info", headers = "Accept=application/json")
  2. public User getUserInfo() {
  3.     // 仅在 Accept 头为 "application/json" 时处理请求
  4.     return userService.getUserInfo();
  5. }
复制代码
2.5 consumes 属性

consumes 属性用于指定请求的 Content-Type 类型。例如,如果你盼望处理的请求体是 JSON 格式,可以这样指定:
  1. @RequestMapping(value = "/create", method = RequestMethod.POST, consumes = "application/json")
  2. public User createUser(@RequestBody User user) {
  3.     // 只接受 Content-Type 为 "application/json" 的请求
  4.     return userService.saveUser(user);
  5. }
复制代码
2.6 produces 属性

produces 属性用于指定返回的响应类型(Content-Type)。例如,如果你盼望返回 JSON 格式的响应,可以这样界说:
  1. @RequestMapping(value = "/user/{id}", produces = "application/json")
  2. public User getUserById(@PathVariable Long id) {
  3.     // 返回 JSON 格式的数据
  4.     return userService.getUserById(id);
  5. }
复制代码
2.7 path 属性

path 是 value 的别名,用于界说请求的 URL 路径。如果需要更具语义化地界说路径,可以利用 path 属性。
  1. @RequestMapping(path = "/details/{id}")
  2. public User getUserDetails(@PathVariable Long id) {
  3.     return userService.getUserDetails(id);
  4. }
复制代码
3. 组合利用

@RequestMapping 可以组合利用多个属性来实现更加复杂的请求匹配。例如:
  1. @RequestMapping(
  2.     value = "/update",
  3.     method = RequestMethod.PUT,
  4.     consumes = "application/json",
  5.     produces = "application/json"
  6. )
  7. public User updateUser(@RequestBody User user) { // @RequestBody 注解用于将请求体中的 JSON 数据转换为 User 对象。
  8.     // 只处理 PUT 请求,且请求体和响应体都是 JSON 格式
  9.     return userService.updateUser(user);
  10. }
复制代码
4. 常见注解简化情势

为了简化常见的 HTTP 操作,Spring 提供了几个注解作为 @RequestMapping 的简化情势:
这些注解都相当于 @RequestMapping 的简化版,直接指定了请求方法类型。
示例:
  1. @GetMapping("/user/{id}")
  2. public User getUser(@PathVariable Long id) {
  3.     return userService.getUserById(id);
  4. }
  5. @PostMapping("/user/create")
  6. public User createUser(@RequestBody User user) {
  7.     return userService.createUser(user);
  8. }
复制代码
4、路径变量和请求参数

4.1 @PathVariable

@PathVariable 注解用于从 URL 的路径中提取数据,并将这些数据绑定到控制器方法的参数上。它通常与 URL 模板中的占位符 {} 一起利用。
1. 基本用法

1.1 单一起径变量

在控制器方法中,可以利用 @PathVariable 注解来提取单个路径变量。
示例:
  1. @RestController
  2. @RequestMapping("/users")
  3. public class UserController {
  4.     @GetMapping("/{id}")
  5.     public String getUser(@PathVariable("id") String id) {
  6.         return "User ID: " + id;
  7.     }
  8. }
复制代码
1.2 多个路径变量

可以在 URL 模板中利用多个路径变量,并在方法中提取这些变量。
示例:
  1. @RestController
  2. @RequestMapping("/users")
  3. public class UserController {
  4.     @GetMapping("/{id}/details/{detailId}")
  5.     public String getUserDetails(@PathVariable("id") String id,
  6.                                  @PathVariable("detailId") String detailId) {
  7.         return "User ID: " + id + ", Detail ID: " + detailId;
  8.     }
  9. }
复制代码
2. URL 模板中的占位符

2.1 单层路径变量

路径变量用于 URL 模板中的单层路径。
示例:
  1. @GetMapping("/products/{productId}")
  2. public String getProduct(@PathVariable("productId") String productId) {
  3.     return "Product ID: " + productId;
  4. }
复制代码
2.2 多层路径变量

路径变量可以用在 URL 的多层路径中,提取差别层次的数据。
示例:
  1. @GetMapping("/categories/{categoryId}/items/{itemId}")
  2. public String getItem(@PathVariable("categoryId") String categoryId,
  3.                       @PathVariable("itemId") String itemId) {
  4.     return "Category ID: " + categoryId + ", Item ID: " + itemId;
  5. }
复制代码
3. 路径变量的名称匹配

示例:
  1. @GetMapping("/items/{itemId}")
  2. public String getItem(@PathVariable("itemId") String id) {
  3.     // 此处 id 是 itemId 的别名,实际名称应与 URL 模板一致
  4.     return "Item ID: " + id;
  5. }
复制代码
4.2 @RequestParam

@RequestParam 是 Spring MVC 中用于处理 HTTP 请求参数的注解。它用于从 HTTP 请求的查询字符串或表单参数中提取数据。它可以用于处理 GET、POST、PUT、DELETE 等请求方法中的参数。
补充:
查询字符串 是附加在 URL 末端的键值对,用于向服务器通报参数。它通常用于 GET 请求,也可以用于其他请求类型。查询字符串以 ? 开始,后面跟随参数键值对,用 & 分隔多个参数。
查询字符串的格式为:
  1. http://example.com/resource?key1=value1&key2=value2
复制代码
表单参数 是通过 HTML 表单提交的键值对,通常在 POST 请求中利用。表单参数可以通过 application/x-www-form-urlencoded 或 multipart/form-data 编码发送,具体取决于表单的内容类型。
表单数据的格式在 application/x-www-form-urlencoded 格式中,表单数据类似于查询字符串:
  1. key1=value1&key2=value2
复制代码
示例

HTML 表单:
  1. <form action="/register" method="post">
  2.     <input type="text" name="username" value="alice">
  3.     <input type="number" name="age" value="30">
  4.     <button type="submit">Register</button>
  5. </form>
复制代码
总结
1. 基本用法

1.1 从查询字符串中获取参数(GET 请求)

当利用 GET 请求时,参数通常附加在 URL 的查询字符串中。
示例:
  1. @RestController
  2. public class UserController {
  3.     @GetMapping("/greet")
  4.     public String greet(@RequestParam("name") String name) {
  5.         return "Hello, " + name + "!";
  6.     }
  7. }
复制代码
1.2 从表单数据中获取参数(POST 请求)

当利用 POST 请求时,参数通常是表单数据的一部分。
示例:
  1. @RestController
  2. public class UserController {
  3.     @PostMapping("/register")
  4.     public String register(@RequestParam("username") String username,
  5.                            @RequestParam("password") String password) {
  6.         return "Registered user: " + username;
  7.     }
  8. }
复制代码
2. @RequestParam 的属性

2.1  value 属性

value(或 name)属性指定请求参数的名称。
示例:
  1. @RequestParam(value = "name") String name
  2. // 或
  3. @RequestParam("name") String name
复制代码
2.2 required 属性

required 属性用于指示参数是否是必须的。默认为 true,如果请求中没有提供该参数,Spring 将抛出异常。如果设置为 false,则可以省略该参数。
示例:
  1. @RequestParam(name = "name", required = false) String name
复制代码
2.3 defaultValue 属性

defaultValue 属性用于指定当请求参数缺失时的默认值。如果参数缺失,则利用默认值而不是抛出异常。
示例:
  1. @RequestParam(name = "name", defaultValue = "Guest") String name
复制代码
3. 路径变量与请求参数的区别

示例:
  1. @RestController
  2. public class UserController {
  3.     @GetMapping("/users/{id}")
  4.     public String getUser(@PathVariable("id") String id) {
  5.         return "User ID: " + id;
  6.     }
  7.     @GetMapping("/search")
  8.     public String search(@RequestParam("query") String query) {
  9.         return "Search query: " + query;
  10.     }
  11. }
复制代码
4. 组合利用

@RequestParam 可以与其他注解(如 @PathVariable、@RequestBody)组合利用,处理复杂的请求。
示例:
  1. @RestController
  2. public class UserController {
  3.     @PostMapping("/users/{id}")
  4.     public String updateUser(@PathVariable("id") String id,
  5.                              @RequestParam("name") String name,
  6.                              @RequestParam(name = "email", required = false) String email) {
  7.         return "Updated user ID: " + id + ", Name: " + name + ", Email: " + (email != null ? email : "Not provided");
  8.     }
  9. }
复制代码
5. 支持的请求内容类型

5、RESTful 风格

RESTful 风格一种基于资源和 HTTP 方法的 Web 服务计划方式,利用标准的 HTTP 动词来处理资源操作,通过 JSON 等格式表示资源状态。它具有可读性高、灵活性强、标准化等长处,是当代 Web 开发中广泛采用的 API 计划模式。
REST 代表 REpresentational State Transfer,意思是通过体现层状态转换举行系统交互。RESTful API 是基于 REST 原则计划的应用程序接口(API),常用于构建面向资源的网络服务。
1. RESTful 风格的核心概念

RESTful 风格主要围绕以下几个核心概念展开:
1.1 资源(Resource)

资源是 REST 的核心,表示网络上的数据或服务。每一个资源都通过一个唯一的 URI(Uniform Resource Identifier,同一资源标识符)来标识。例如:
资源通常以 名词 来定名,并且在 URI 中尽量避免利用动词。
1.2 HTTP 方法

RESTful API 利用标准的 HTTP 方法来对资源执行差别的操作,每个方法都与一个特定的动作语义相关:
1.3 状态表示(Representation)

当客户端请求一个资源时,服务器将该资源的当前状态通过 状态表示 发送给客户端。通常,RESTful API 利用 JSONXML 来表示资源的状态。例如,获取用户资源可能会返回这样的 JSON 数据:
  1. {
  2.     "id": 1,
  3.     "name": "Alice",
  4.     "email": "alice@example.com"
  5. }
复制代码
1.4 无状态性(Stateless)

RESTful 服务是 无状态的,这意味着每个请求都应该包含所有必要的信息,服务器不会在差别请求之间保留任何客户端的状态。每次请求都是独立的,服务器不会依赖之前请求中的信息。
1.5 URI 的结构化

RESTful API 的 URI 通常是结构化的,以层级关系表示资源。例如:
2. RESTful 风格的计划原则

RESTful API 计划遵照以下几个原则:
2.1 资源的标识通过 URI

每个资源都应该有唯一的 URI,并且通过这个 URI 可以对资源举行操作。例如,用户资源 /users 的 URI 是 /users,单个用户资源 /users/1 的 URI 是 /users/1。
2.2 利用 HTTP 方法明确资源操作

HTTP 方法(GET、POST、PUT、DELETE 等)应该明确表示对资源的操作,避免在 URI 中利用动词。例如,不推荐利用 /getUser 或 /deleteUser 这样的 URI,应该利用 /users 并联合 HTTP 方法来实现。
2.3 利用 JSON 或 XML 表示资源

资源的状态表示通常利用标准的格式,好比 JSON 或 XML,其中 JSON 更常用,因为它简洁、易于剖析。响应体中包含资源的状态信息,并返回给客户端。
2.4 无状态的请求

每次客户端与服务器之间的交互都是独立的,服务器不保留客户端的状态。请求中的所有必要信息(如认证信息)都应该包含在每次请求中。
2.5 遵照标准的状态码

RESTful API 应该利用标准的 HTTP 状态码来反映操作的效果:
3. RESTful 风格的示例

假设我们计划一个管理用户的 RESTful API,以下是一些操作和对应的 RESTful URL 和 HTTP 方法:
操作HTTP 方法URL请求体响应体获取所有效户GET/users无用户列表 (JSON)获取特定用户(ID = 1)GET/users/1无用户信息 (JSON)创建新用户POST/users用户信息 (JSON)新建用户信息更新特定用户(ID = 1)PUT/users/1更新后的用户信息更新后的用户信息删除特定用户(ID = 1)DELETE/users/1无无4. RESTful 与传统 API 的对比

5. RESTful API 的优势

6. RESTful API 的局限

6、数据返回格式

6.1 Json

在前后端分离的模式下,数据的返回格式通常是 JSON(默认),Spring MVC 利用 Jackson 或 Gson 等库来自动将 Java 对象转换为 JSON 格式。
示例:
  1. @GetMapping("/user/{id}")
  2. public User getUser(@PathVariable Long id) {
  3.     return new User(id, "Alice", 30);  // 自动返回 JSON 数据
  4. }
复制代码
返回的 JSON 数据格式如下:
  1. {
  2.   "id": 1,
  3.   "name": "Alice",
  4.   "age": 30
  5. }
复制代码
maven 官网 jackson 依赖

6.2 RequestEntity

RequestEntity 是 Spring 中用来封装 HTTP 请求信息的类,它继续自 HttpEntity,并扩展了对 HTTP 请求头、HTTP 方法(如 GET、POST)、URL 等的支持。RequestEntity 可以用来在处理请求时,获取完整的请求信息,例如请求头、请求体、请求方法等。
核心属性

RequestEntity 常用方法

RequestEntity 示例

假设我们需要创建一个处理 POST 请求的接口,可以利用 RequestEntity 获取请求的所有细节,包罗请求头、请求体和请求方法。
示例代码:
  1. @PostMapping("/process")
  2. public ResponseEntity<String> handleRequestEntity(RequestEntity<String> requestEntity) {
  3.     // 获取请求方法
  4.     HttpMethod method = requestEntity.getMethod();
  5.    
  6.     // 获取请求头
  7.     HttpHeaders headers = requestEntity.getHeaders();
  8.    
  9.     // 获取请求体
  10.     String body = requestEntity.getBody();
  11.    
  12.     // 打印请求信息
  13.     System.out.println("Method: " + method);
  14.     System.out.println("Headers: " + headers);
  15.     System.out.println("Body: " + body);
  16.    
  17.     return ResponseEntity.ok("Request processed successfully");
  18. }
复制代码
请求示例:
  1. curl -X POST http://localhost:8080/process -H "Content-Type: text/plain" -d "Hello, Spring!"
复制代码
输出:
  1. Method: POST
  2. Headers: [Content-Type:"text/plain"]
  3. Body: Hello, Spring!
复制代码
在这个例子中,RequestEntity 提供了对请求的详细信息,便于处理复杂的请求逻辑。
构造 RequestEntity

RequestEntity 也可以通过静态工厂方法来创建,例如:
  1. RequestEntity<String> requestEntity = RequestEntity
  2.         .post(new URI("http://localhost:8080/process"))
  3.         .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
  4.         .body("{"key": "value"}");
复制代码
这里我们通过 RequestEntity 构造了一个 POST 请求,设置了请求头和请求体。
RequestEntity 的实用场景

6.3 ResponseEntity

ResponseEntity 是 Spring 中用来封装 HTTP 响应信息的类,它同样继续自 HttpEntity,扩展了对 HTTP 响应头、响应状态码的支持。ResponseEntity 答应我们更灵活地设置返回给客户端的响应,包罗状态码、响应体、响应头等。
核心属性

ResponseEntity 常用方法

ResponseEntity 示例

假设我们需要处理一个文件上传操作,并返回差别的响应状态码来表示上传的效果:
示例代码:
  1. @PostMapping("/upload")
  2. public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
  3.     if (file.isEmpty()) {
  4.         return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("文件为空");
  5.     }
  6.     try {
  7.         // 假设保存文件到指定路径
  8.         String uploadDir = "/path/to/upload/directory/";
  9.         file.transferTo(new File(uploadDir + file.getOriginalFilename()));
  10.         return ResponseEntity.ok("文件上传成功");
  11.     } catch (IOException e) {
  12.         return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件上传失败");
  13.     }
  14. }
复制代码
请求示例:
  1. curl -X POST -F "file=@/path/to/file.txt" http://localhost:8080/upload
复制代码
可能的响应:

在这个例子中,ResponseEntity 用来灵活地返回差别的状态码和消息。
构造 ResponseEntity

你可以通过静态工厂方法来构建 ResponseEntity,例如:
  1. ResponseEntity<String> response = ResponseEntity
  2.         .status(HttpStatus.CREATED)
  3.         .header(HttpHeaders.LOCATION, "/new-resource")
  4.         .body("资源创建成功");
复制代码
这个例子返回了一个 201 Created 的响应,同时设置了 Location 响应头,指向新创建的资源路径。
ResponseEntity 的实用场景

6.4 RequestEntity 和 ResponseEntity 的关系

RequestEntity 主要用于处理 HTTP 请求,资助我们获取请求的所有详细信息;而 ResponseEntity 则用于构建和返回 HTTP 响应,资助我们返回自界说的响应数据、头和状态码。
常见组合利用场景

在 Spring MVC 的控制器方法中,你可以同时利用 RequestEntity 来获取请求信息,利用 ResponseEntity 返反响应。例如:
  1. @PostMapping("/process-data")
  2. public ResponseEntity<String> handleRequest(RequestEntity<String> requestEntity) {
  3.     // 获取请求体
  4.     String requestBody = requestEntity.getBody();
  5.    
  6.     // 处理逻辑...
  7.    
  8.     // 返回响应
  9.     return ResponseEntity.ok("处理成功:" + requestBody);
  10. }
复制代码
7、文件的上传和下载

在 Spring MVC 中,文件上传和下载是常见的需求,通常通过 MultipartFile 和 HttpServletResponse 等组件来处理。下面我们来详细讲解文件上传和下载的实现。
1. 文件上传

1.1 利用 MultipartFile 实现文件上传

Spring MVC 提供了 MultipartFile 接口来处理文件上传。为了支持文件上传,起首需要在项目的设置文件中启用对多部分请求的支持。
1.2 设置 Spring Boot 以支持文件上传

在 application.properties 或 application.yml 中设置最大文件上传巨细:
  1. spring.servlet.multipart.max-file-size=10MB
  2. spring.servlet.multipart.max-request-size=15MB
复制代码
1.3 文件上传的 Controller 示例
  1. import org.springframework.web.bind.annotation.*;
  2. import org.springframework.web.multipart.MultipartFile;
  3. import org.springframework.http.ResponseEntity;
  4. import org.springframework.http.HttpStatus;
  5. import java.io.File;
  6. import java.io.IOException;
  7. @RestController
  8. @RequestMapping("/files")
  9. public class FileUploadController {
  10.     @PostMapping("/upload")
  11.     public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
  12.         // 检查文件是否为空
  13.         if (file.isEmpty()) {
  14.             return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("文件为空");
  15.         }
  16.         // 获取文件名
  17.         String fileName = file.getOriginalFilename();
  18.         try {
  19.             // 文件保存路径(可以根据需求修改)
  20.             String uploadDir = "/path/to/upload/directory/";
  21.             File dest = new File(uploadDir + fileName);
  22.             // 保存文件到服务器
  23.             file.transferTo(dest);
  24.             return ResponseEntity.ok("文件上传成功:" + fileName);
  25.         } catch (IOException e) {
  26.             e.printStackTrace();
  27.             return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件上传失败");
  28.         }
  29.     }
  30. }
复制代码
1.4 前端文件上传表单示例
  1. <form method="POST" enctype="multipart/form-data" action="/files/upload">
  2.     <input type="file" name="file"/>
  3.     <button type="submit">上传文件</button>
  4. </form>
复制代码
在这个例子中,文件上传表单利用 enctype="multipart/form-data" 举行编码,使文件能被正确地传输到服务器。
2. 文件下载

文件下载通常通过 HttpServletResponse 来设置响应头并将文件的内容写入到输出流中。
2.1 文件下载的 Controller 示例
  1. import org.springframework.web.bind.annotation.*;
  2. import javax.servlet.http.HttpServletResponse;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.IOException;
  6. import java.io.OutputStream;
  7. @RestController
  8. @RequestMapping("/files")
  9. public class FileDownloadController {
  10.     @GetMapping("/download/{fileName}")
  11.     public void downloadFile(@PathVariable("fileName") String fileName, HttpServletResponse response) {
  12.         // 文件存储路径
  13.         String filePath = "/path/to/upload/directory/" + fileName;
  14.         File file = new File(filePath);
  15.         if (!file.exists()) {
  16.             response.setStatus(HttpServletResponse.SC_NOT_FOUND);
  17.             return;
  18.         }
  19.         // 设置响应头
  20.         response.setContentType("application/octet-stream");
  21.         response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
  22.         response.setContentLengthLong(file.length());
  23.         try (FileInputStream fis = new FileInputStream(file);
  24.              OutputStream os = response.getOutputStream()) {
  25.             // 将文件数据写入输出流
  26.             byte[] buffer = new byte[1024];
  27.             int bytesRead;
  28.             while ((bytesRead = fis.read(buffer)) != -1) {
  29.                 os.write(buffer, 0, bytesRead);
  30.             }
  31.             os.flush();
  32.         } catch (IOException e) {
  33.             response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
  34.         }
  35.     }
  36. }
复制代码
2.2 前端文件下载链接示例
  1. <a target="_blank" href="https://www.cnblogs.com/files/download/sample.txt">下载文件</a>
复制代码
在这个示例中,当用户点击链接时,欣赏器会发出下载请求,并触发 downloadFile 方法。该方法会将服务器上的文件内容以流的情势发送到客户端。
3. 文件上传与下载的详细解释

3.1 文件上传

3.2 文件下载

4. 注意事项

5. 总结

Spring MVC 提供了简洁的方式来处理文件上传和下载。通过利用 MultipartFile 和 HttpServletResponse,开发者可以轻松实现文件上传和下载的功能。
8、拦截器

在 Spring MVC 中,拦截器(Interceptor)是一个用于在请求处理的差别阶段举行拦截和处理的机制。拦截器可以拦截进入控制器之前、控制器执行之后以及请求完成之后的操作,并答应开发者在这些过程中自界说逻辑。拦截器的利用使得我们可以实现诸如权限验证、日志记录、数据预处理、响应数据后处理等功能。
拦截器类似于过滤器(Filter),但与过滤器差别的是,拦截器专注于与 Spring MVC 的请求处理流程联合得更紧密,并且可以或许直接访问 Spring 的上下文和数据。
1. 拦截器的工作原理

拦截器通过实现 HandlerInterceptor 接口来实现,它界说了三个主要的回调方法,分别对应请求处理的差别阶段:
三个主要方法:

2. 怎样实现拦截器

步骤一:创建拦截器类

拦截器类需要实现 HandlerInterceptor 接口。
示例拦截器实现
  1. import javax.servlet.http.HttpServletRequest;
  2. import javax.servlet.http.HttpServletResponse;
  3. import org.springframework.web.servlet.HandlerInterceptor;
  4. import org.springframework.web.servlet.ModelAndView;
  5. public class MyInterceptor implements HandlerInterceptor {
  6.     // 进入控制器之前
  7.     @Override
  8.     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  9.         System.out.println("preHandle: 请求前检查权限...");
  10.         
  11.         // 可以进行权限验证逻辑,返回 false 则拦截请求
  12.         String authToken = request.getHeader("Authorization");
  13.         if (authToken == null || !authToken.equals("VALID_TOKEN")) {
  14.             response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
  15.             return false; // 拦截请求
  16.         }
  17.         return true; // 继续处理请求
  18.     }
  19.     // 控制器执行后,视图渲染前
  20.     @Override
  21.     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  22.         System.out.println("postHandle: 可以修改 ModelAndView...");
  23.     }
  24.     // 请求结束,视图渲染完成后
  25.     @Override
  26.     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  27.         System.out.println("afterCompletion: 资源清理...");
  28.     }
  29. }
复制代码
步骤二:注册拦截器

创建好拦截器类后,我们需要将其注册到 Spring 的拦截器链中。通常,我们通过设置类来注册拦截器。
示例设置
  1. import org.springframework.context.annotation.Configuration;
  2. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  3. import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
  4. @Configuration
  5. public class WebConfig implements WebMvcConfigurer {
  6.     @Override
  7.     public void addInterceptors(InterceptorRegistry registry) {
  8.         // 注册拦截器
  9.         registry.addInterceptor(new MyInterceptor())
  10.                 .addPathPatterns("/**") // 拦截所有路径
  11.                 .excludePathPatterns("/login", "/public/**"); // 排除某些路径
  12.     }
  13. }
复制代码
在这个设置中,拦截器 MyInterceptor 被注册到拦截器链中,并指定拦截所有请求路径(/**),同时排除 /login 和 /public/** 的路径不被拦截。
3. 拦截器与过滤器的区别

相同点

差别点

拦截器 (Interceptor)过滤器 (Filter)作用于 Spring MVC 的请求处理流程,与 Spring 的上下文联合紧密。作用于整个 web 应用的请求链,与 Spring 无关。可以获取控制器信息,并且可以操作 ModelAndView。只能操作原生的 HttpServletRequest 和 HttpServletResponse,不涉及 Spring MVC 的特性。实现 HandlerInterceptor 接口。实现 javax.servlet.Filter 接口。常用于 RESTful API 中的数据预处理、后处理。常用于全局的过滤、编码处理、安全检查等。4. 拦截器的常见应用场景

1. 权限验证

通过 preHandle 方法在请求进入控制器之前拦截请求,举行权限验证或认证处理。如果验证失败,可以返回相应的错误响应,终止请求继续执行。
示例:
  1. @Override
  2. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  3.     String authToken = request.getHeader("Authorization");
  4.     if (authToken == null || !isValidToken(authToken)) {
  5.         response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
  6.         return false;
  7.     }
  8.     return true;
  9. }
复制代码
2. 日志记录

在 preHandle 或 afterCompletion 中记录请求的日志,包罗请求的 URL、参数、处理时间等。
示例:
  1. long startTime = System.currentTimeMillis();
  2. request.setAttribute("startTime", startTime);
  3. @Override
  4. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  5.     long startTime = (Long) request.getAttribute("startTime");
  6.     long endTime = System.currentTimeMillis();
  7.     System.out.println("Request URL: " + request.getRequestURL() + " - Time Taken: " + (endTime - startTime) + "ms");
  8. }
复制代码
3. 数据预处理

在 preHandle 中举行数据的预处理,例如对请求参数举行格式化、数据补充等。
示例:
  1. @Override
  2. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  3.     String param = request.getParameter("date");
  4.     if (param != null) {
  5.         // 预处理日期参数
  6.         LocalDate date = LocalDate.parse(param, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
  7.         request.setAttribute("formattedDate", date);
  8.     }
  9.     return true;
  10. }
复制代码
4. 数据后处理

在 postHandle 中对控制器返回的 ModelAndView 举行修改,添加公共的页面元素或数据。
示例:
  1. @Override
  2. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  3.     if (modelAndView != null) {
  4.         modelAndView.addObject("footerMessage", "版权所有 © 2024");
  5.     }
  6. }
复制代码
5. 拦截器执行次序

如果有多个拦截器时,它们的执行次序是根据注册时的次序来决定的。所有 preHandle 方法按照次序依次执行,所有 postHandle 和 afterCompletion 方法按照相反的次序执行。
假设有两个拦截器 Interceptor1 和 Interceptor2,它们的执行次序如下:
6. 总结

9、跨域支持(CORS)

前后端分离时,前端和后端通常部署在差别的服务器上,这会导致跨域问题。Spring MVC 提供了多种跨域设置方式,最常见的是利用 @CrossOrigin 注解。
示例:
  1. @RestController
  2. @CrossOrigin(origins = "http://localhost:8080")
  3. @RequestMapping("/api/users")
  4. public class UserController {
  5.     // 控制器方法
  6. }
复制代码
或者可以在全局设置跨域:
  1. @Configuration
  2. public class WebConfig implements WebMvcConfigurer {
  3.     @Override
  4.     public void addCorsMappings(CorsRegistry registry) {
  5.         registry.addMapping("/**")
  6.                 .allowedOrigins("http://localhost:8080")
  7.                 .allowedMethods("GET", "POST", "PUT", "DELETE");
  8.     }
  9. }
复制代码
8、常见开发模式

在前后端分离中,Spring MVC 通常与 RESTful API 模式联合利用,前端通过 HTTP 方法调用后端的 API 接口。后端仅负责数据处理和业务逻辑,而前端负责用户界面、数据展示和交互。
9、总结

在前后端分离模式下,Spring MVC 的主要职责是处理请求、举行业务逻辑处理、返回 JSON 或 XML 格式的数据。其核心组件如 @RestController、@RequestMapping、@ResponseBody 等,简化了 RESTful API 的开发流程。此外,Spring MVC 还提供了强大的异常处理机制和跨域支持,使前后端分离的开发更加顺畅。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4