spring mvc详细讲解(前后端分离模式)

打印 上一主题 下一主题

主题 884|帖子 884|积分 2652

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

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

  • 客户端发送请求:前端应用(如 Vue.js 或 React)通过 AJAX 或 Axios 向后端发送 HTTP 请求,通常是 RESTful API 请求。
  • DispatcherServlet 拦截请求:DispatcherServlet 作为 Spring MVC 的前端控制器,拦截所有的 HTTP 请求,并将请求转发给合适的处理器(Controller)。
  • 处理请求:Controller 根据请求路径和请求参数,调用业务逻辑处理或数据库操作,生成相应的响应数据。
  • 返回 JSON 或 XML 数据:Controller 将处理效果作为 JSON 或 XML 格式的数据返回给客户端。前端应用再根据返回的数据举行页面更新或其他操作。
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


  • @RequestBody:用于从请求体中提取 JSON 或 XML 格式的数据并映射到方法参数上。前端通常发送 JSON 格式的数据,Spring MVC 会自动将其转换为 Java 对象。
  • @ResponseBody:用于将方法的返回值自动序列化为 JSON 或 XML 格式返回给客户端。方法的返回值会直接作为 HTTP 响应体返回,而不是视图名称。
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 的简化情势:

  • @GetMapping:用于处理 GET 请求
  • @PostMapping:用于处理 POST 请求
  • @PutMapping:用于处理 PUT 请求
  • @DeleteMapping:用于处理 DELETE 请求
  • @PatchMapping:用于处理 PATCH 请求
这些注解都相当于 @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. }
复制代码

  • 请求 URL:/users/123
  • id 变量将被设置为 123,效果:User ID: 123
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. }
复制代码

  • 请求 URL:/users/123/details/456
  • id 变量将被设置为 123,detailId 变量将被设置为 456,效果:User ID: 123, Detail ID: 456
2. URL 模板中的占位符

2.1 单层路径变量

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

  • URL:/products/789
  • productId 变量将被设置为 789
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. }
复制代码

  • URL:/categories/1/items/42
  • categoryId 变量将被设置为 1,itemId 变量将被设置为 42
3. 路径变量的名称匹配


  • @PathVariable 的名称需要与 URL 模板中的占位符名称匹配。如果不匹配,会导致绑定失败。
示例:
  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>
复制代码

  • username 的值是 alice。
  • age 的值是 30。
  • 表单提交的数据:username=alice&age=30
  • 返回效果:Registered user: alice, Age: 30
总结

  • 查询字符串 是在 URL 中附加的参数,用于 GET 请求。
  • 表单参数 是通过 HTML 表单提交的参数,通常用于 POST 请求。
  • 在 Spring MVC 中,@RequestParam 注解可以用于提取这两种类型的请求参数。
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. }
复制代码

  • 请求 URL:/greet?name=Alice
  • 效果:Hello, Alice!
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. }
复制代码

  • 表单数据:
    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>
    复制代码
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
复制代码

  • 如果请求中不包含 name 参数,name 将被设置为 null,而不是抛出异常。
2.3 defaultValue 属性

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

  • 如果请求中不包含 name 参数,name 将被设置为 "Guest"。
3. 路径变量与请求参数的区别


  • 路径变量(Path Variables):用于从 URL 的路径中提取数据。例如:/users/{id}。
  • 请求参数(Request Parameters):用于从查询字符串或表单数据中提取数据。例如:/users?id=1。
示例:
  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. }
复制代码

  • /users/1 利用路径变量,id 是路径的一部分。
  • /search?query=spring 利用请求参数,query 是查询字符串的一部分。
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. 支持的请求内容类型


  • application/x-www-form-urlencoded:传统的表单提交方式。
  • multipart/form-data:用于文件上传等复杂表单数据。
  • application/json:通常与 @RequestBody 联合利用,但在特定情况下也可以用于 @RequestParam。
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,同一资源标识符)来标识。例如:

  • http://example.com/users:表示用户聚集资源。
  • http://example.com/users/1:表示具体用户资源,用户 ID 为 1。
资源通常以 名词 来定名,并且在 URI 中尽量避免利用动词。
1.2 HTTP 方法

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

  • GET:从服务器获取资源。

    • 示例:GET /users 获取所有效户,GET /users/1 获取 ID 为 1 的用户。

  • POST:向服务器创建一个新的资源。

    • 示例:POST /users 在用户聚集中创建一个新用户。

  • PUT:更新服务器上的资源(通常是更换整个资源)。

    • 示例:PUT /users/1 更新 ID 为 1 的用户信息。

  • PATCH:部分更新服务器上的资源(通常是更新资源的某些字段)。

    • 示例:PATCH /users/1 更新 ID 为 1 的用户的部分信息。

  • DELETE:从服务器上删除资源。

    • 示例:DELETE /users/1 删除 ID 为 1 的用户。

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 通常是结构化的,以层级关系表示资源。例如:

  • /users:表示所有效户。
  • /users/1:表示 ID 为 1 的用户。
  • /users/1/orders:表示 ID 为 1 的用户的订单。
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 状态码来反映操作的效果:

  • 200 OK:请求乐成并返回数据。
  • 201 Created:乐成创建资源。
  • 204 No Content:操作乐成但不返回数据(如 DELETE 请求)。
  • 400 Bad Request:客户端发送的请求无效(如参数错误)。
  • 401 Unauthorized:未经授权的请求。
  • 404 Not Found:请求的资源不存在。
  • 500 Internal Server Error:服务器内部错误。
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 的对比


  • RESTful API:基于资源和 HTTP 方法,简洁、标准化、易于理解和扩展。请求和响应体通常利用 JSON,客户端和服务器之间的通信是无状态的。
  • 传统 API:偶然会在 URL 中包含动词,例如 /getUser、/deleteUser,并且可能不遵照 HTTP 方法的语义。
5. RESTful API 的优势


  • 可读性强:基于资源的计划和标准的 HTTP 方法,使得 API 直观且易于理解。
  • 灵活性高:RESTful API 可以支持多种客户端(如 Web、移动装备)。
  • 无状态性:减少服务器的负担,因为它不需要保存客户端的状态。
  • 标准化:利用标准的 HTTP 协议,容易被各种工具和平台集成。
6. RESTful API 的局限


  • 无状态性限制:无状态性虽然简化了请求,但在一些需要持续状态的操作(如登录状态)中需要额外计划。
  • 复杂查询场景:对于复杂的查询场景,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 可以用来在处理请求时,获取完整的请求信息,例如请求头、请求体、请求方法等。
核心属性


  • HTTP 请求头 (Headers):可以通过 RequestEntity 获取请求中的 HTTP 头信息。
  • HTTP 请求方法 (Method):可以通过 RequestEntity 知晓请求是 GET、POST、PUT、DELETE 等方法。
  • 请求 URL:RequestEntity 包含完整的请求 URL,便于在处理请求时获取 URL 参数。
  • 请求体 (Body):对于 POST、PUT 等带有请求体的方法,可以通过 RequestEntity 获取请求体中的数据。
RequestEntity 常用方法


  • getMethod():获取 HTTP 请求方法。
  • getHeaders():获取请求头。
  • getBody():获取请求体。
  • getUrl():获取请求的 URL。
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 的实用场景


  • 需要访问 HTTP 请求的完整信息:当你需要获取请求的所有细节(包罗方法、头、URL、体)时,RequestEntity 非常有效。
  • 复杂的 API 请求处理:在处理 REST API 请求时,它提供了获取和操作请求细节的本领。
6.3 ResponseEntity

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


  • HTTP 响应头 (Headers):可以通过 ResponseEntity 设置或获取返回给客户端的 HTTP 头信息。
  • HTTP 响应体 (Body):可以通过 ResponseEntity 设置返回给客户端的响应数据。
  • HTTP 状态码 (Status Code):可以通过 ResponseEntity 设置 HTTP 响应的状态码(如 200 OK,404 Not Found,500 Internal Server Error 等)。
ResponseEntity 常用方法


  • status(HttpStatus):设置响应状态码。
  • body(Object):设置响应体内容。
  • header(String, String):设置响应头。
  • ok():返回状态码 200 的响应。
  • notFound():返回状态码 404 的响应。
  • badRequest():返回状态码 400 的响应。
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
复制代码
可能的响应:


  • 乐成时:200 OK,响应体为 "文件上传乐成"
  • 文件为空时:400 Bad Request,响应体为 "文件为空"
  • 出现服务器错误时:500 Internal Server Error,响应体为 "文件上传失败"
在这个例子中,ResponseEntity 用来灵活地返回差别的状态码和消息。
构造 ResponseEntity

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


  • 需要灵活设置响应:当你需要自界说响应的状态码、头、体时,ResponseEntity 提供了很好的灵活性。
  • RESTful API 响应:在 REST API 开发中,它是一个常见的选择,便于构建符合 HTTP 标准的响应。
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
复制代码

  • max-file-size:限制单个文件的巨细。
  • max-request-size:限制整个请求的巨细。
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 文件上传


  • MultipartFile:Spring MVC 提供的接口,用于处理上传的文件。你可以通过 @RequestParam("file") 获取上传的文件。
  • transferTo:MultipartFile 提供的一个方法,用于将上传的文件保存到指定路径。
  • 表单的 enctype:文件上传表单的 enctype="multipart/form-data",它答应文件和其他数据一起被发送。
3.2 文件下载


  • HttpServletResponse:用于生成 HTTP 响应,包罗设置响应头和写入响应体。
  • Content-Type:指定响应的内容类型为 application/octet-stream,表示该响应是一个二进制流。
  • Content-Disposition:用于指示欣赏器将响应内容作为附件下载,而不是直接显示。例如,attachment; filename="file.txt" 会强制欣赏器下载文件,并将其定名为 file.txt。
  • 流操作:通过 FileInputStream 读取文件内容,并通过 OutputStream 将内容写入响应体中。
4. 注意事项


  • 文件路径安全性:在处理文件路径时,确保不要袒露服务器文件系统的敏感信息。发起利用文件存储服务或数据库来管理上传的文件路径。
  • 文件上传巨细限制:在实际生产环境中,除了在 Spring 设置文件中设置文件上传巨细限制之外,也可以通过 Web 服务器(如 Tomcat、Nginx)举行限制。
  • 异常处理:在文件上传和下载过程中,可能会出现各种异常(如文件不存在、权限问题等),应对这些异常举行适当的处理。
5. 总结

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

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

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


  • preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)

    • 时机:在请求到达控制器之前执行(即在执行控制器方法之前)。
    • 作用:用于处理一些前置逻辑,如认证、权限检查、日志记录等。
    • 返回值:返回 true 时继续执行下一个拦截器或进入控制器方法,返回 false 则中断请求处理,不会继续执行后续拦截器或控制器。

  • postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView)

    • 时机:在控制器方法执行之后,视图渲染之前执行。
    • 作用:可以修改控制器返回的 ModelAndView,对数据举行二次处理,或者决定渲染的视图。

  • afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

    • 时机:在整个请求完成之后(视图渲染完成后)执行。
    • 作用:用于举行一些资源清理、日志记录、异常处理等工作。

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,它们的执行次序如下:

  • Interceptor1.preHandle() -> Interceptor2.preHandle() -> 控制器方法 -> Interceptor2.postHandle() -> Interceptor1.postHandle() -> Interceptor2.afterCompletion() -> Interceptor1.afterCompletion()
6. 总结


  • HandlerInterceptor 是 Spring MVC 中的拦截器接口,它通过 preHandle、postHandle 和 afterCompletion 三个方法来拦截请求的差别阶段。
  • 拦截器通常用于权限验证、日志记录、数据预处理和后处理等场景。
  • 与过滤器相比,拦截器与 Spring MVC 紧密集成,可以或许获取
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 接口。后端仅负责数据处理和业务逻辑,而前端负责用户界面、数据展示和交互。

  • 前端利用 Axios 或 Fetch 调用后端 API:
    前端通过 AJAX 向后端发送请求:
    1. axios.get('/api/users/1')
    2.     .then(response => {
    3.         console.log(response.data);
    4.     })
    5.     .catch(error => {
    6.         console.error(error);
    7.     });
    复制代码
  • 后端返回 JSON 数据:
    后端 Controller 处理请求并返回 JSON 数据,前端接收数据后举行页面更新。
9、总结

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

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

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

盛世宏图

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表