在前后端分离模式下,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 注解来简化开发,它表示这个控制器只会返回数据而不是视图。
示例:- @RestController
- @RequestMapping("/api/users")
- public class UserController {
- @GetMapping("/{id}")
- public User getUserById(@PathVariable Long id) {
- // 模拟从数据库获取用户
- User user = new User(id, "John", 25);
- return user; // 返回 JSON 数据
- }
- @PostMapping("/create")
- public User createUser(@RequestBody User user) {
- // 保存用户逻辑
- return user; // 返回保存后的用户信息
- }
- }
复制代码 在这个例子中,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 路径前缀,所有在此类中的方法共享这个前缀。
示例:- @RestController
- @RequestMapping("/api/users")
- public class UserController {
- // 所有路径都会以 "/api/users" 为前缀
- }
复制代码 方法级别的 @RequestMapping
方法级别的 @RequestMapping 用于指定具体的请求路径和操作,可以与类级别的路径组合形成完整的 URL。
示例:- @RestController
- @RequestMapping("/api/users")
- public class UserController {
- @RequestMapping("/list")
- public List<User> getAllUsers() {
- // 处理 "/api/users/list" 请求
- return userService.getAllUsers();
- }
- @RequestMapping("/details/{id}")
- public User getUserById(@PathVariable Long id) {
- // 处理 "/api/users/details/{id}" 请求
- return userService.getUserById(id);
- }
- }
复制代码 2. @RequestMapping 的常用属性
@RequestMapping 提供了多个属性用于更加精确地匹配 HTTP 请求:
2.1 value 属性
value 属性指定请求的路径,可以是一个字符串数组,表示答应多个 URL 访问同一个处理器方法。- @RequestMapping(value = {"/list", "/all"})
- public List<User> getAllUsers() {
- // "/list" 或 "/all" 都会被映射到这个方法
- return userService.getAllUsers();
- }
复制代码 2.2 method 属性
method 属性用于指定支持的 HTTP 请求方法,例如 GET、POST、PUT、DELETE 等。- @RequestMapping(value = "/create", method = RequestMethod.POST)
- public User createUser(@RequestBody User user) {
- // 只处理 POST 请求
- return userService.saveUser(user);
- }
复制代码 如果你盼望更简洁地界说请求方法,可以利用特定的注解替代 @RequestMapping,如 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping。
2.3 params 属性
params 属性可以用于限制请求参数的存在与否。例如,如果你盼望方法仅在某个特定参数存在时被调用,可以利用 params 属性。- http://example.com/login?username=yourUsername&password=yourPassword
- @RequestMapping(value = "/search", params = {"username", "password"})
- public List<User> searchUsersByName(@RequestParam String username, @RequestParam String password) {
- // 只有请求中包含 username 参数时,这个方法才会被执行
- return userService.searchByName(username);
- }
复制代码 2.4 headers 属性
headers 属性用于根据 HTTP 请求头匹配请求。这个属性可以用来在某些特定的请求头存在时执行某个处理方法。- @RequestMapping(value = "/info", headers = "Accept=application/json")
- public User getUserInfo() {
- // 仅在 Accept 头为 "application/json" 时处理请求
- return userService.getUserInfo();
- }
复制代码 2.5 consumes 属性
consumes 属性用于指定请求的 Content-Type 类型。例如,如果你盼望处理的请求体是 JSON 格式,可以这样指定:- @RequestMapping(value = "/create", method = RequestMethod.POST, consumes = "application/json")
- public User createUser(@RequestBody User user) {
- // 只接受 Content-Type 为 "application/json" 的请求
- return userService.saveUser(user);
- }
复制代码 2.6 produces 属性
produces 属性用于指定返回的响应类型(Content-Type)。例如,如果你盼望返回 JSON 格式的响应,可以这样界说:- @RequestMapping(value = "/user/{id}", produces = "application/json")
- public User getUserById(@PathVariable Long id) {
- // 返回 JSON 格式的数据
- return userService.getUserById(id);
- }
复制代码 2.7 path 属性
path 是 value 的别名,用于界说请求的 URL 路径。如果需要更具语义化地界说路径,可以利用 path 属性。- @RequestMapping(path = "/details/{id}")
- public User getUserDetails(@PathVariable Long id) {
- return userService.getUserDetails(id);
- }
复制代码 3. 组合利用
@RequestMapping 可以组合利用多个属性来实现更加复杂的请求匹配。例如:- @RequestMapping(
- value = "/update",
- method = RequestMethod.PUT,
- consumes = "application/json",
- produces = "application/json"
- )
- public User updateUser(@RequestBody User user) { // @RequestBody 注解用于将请求体中的 JSON 数据转换为 User 对象。
- // 只处理 PUT 请求,且请求体和响应体都是 JSON 格式
- return userService.updateUser(user);
- }
复制代码 4. 常见注解简化情势
为了简化常见的 HTTP 操作,Spring 提供了几个注解作为 @RequestMapping 的简化情势:
- @GetMapping:用于处理 GET 请求
- @PostMapping:用于处理 POST 请求
- @PutMapping:用于处理 PUT 请求
- @DeleteMapping:用于处理 DELETE 请求
- @PatchMapping:用于处理 PATCH 请求
这些注解都相当于 @RequestMapping 的简化版,直接指定了请求方法类型。
示例:- @GetMapping("/user/{id}")
- public User getUser(@PathVariable Long id) {
- return userService.getUserById(id);
- }
- @PostMapping("/user/create")
- public User createUser(@RequestBody User user) {
- return userService.createUser(user);
- }
复制代码 4、路径变量和请求参数
4.1 @PathVariable
@PathVariable 注解用于从 URL 的路径中提取数据,并将这些数据绑定到控制器方法的参数上。它通常与 URL 模板中的占位符 {} 一起利用。
1. 基本用法
1.1 单一起径变量
在控制器方法中,可以利用 @PathVariable 注解来提取单个路径变量。
示例:- @RestController
- @RequestMapping("/users")
- public class UserController {
- @GetMapping("/{id}")
- public String getUser(@PathVariable("id") String id) {
- return "User ID: " + id;
- }
- }
复制代码
- 请求 URL:/users/123
- id 变量将被设置为 123,效果:User ID: 123
1.2 多个路径变量
可以在 URL 模板中利用多个路径变量,并在方法中提取这些变量。
示例:- @RestController
- @RequestMapping("/users")
- public class UserController {
- @GetMapping("/{id}/details/{detailId}")
- public String getUserDetails(@PathVariable("id") String id,
- @PathVariable("detailId") String detailId) {
- return "User ID: " + id + ", Detail ID: " + detailId;
- }
- }
复制代码
- 请求 URL:/users/123/details/456
- id 变量将被设置为 123,detailId 变量将被设置为 456,效果:User ID: 123, Detail ID: 456
2. URL 模板中的占位符
2.1 单层路径变量
路径变量用于 URL 模板中的单层路径。
示例:- @GetMapping("/products/{productId}")
- public String getProduct(@PathVariable("productId") String productId) {
- return "Product ID: " + productId;
- }
复制代码
- URL:/products/789
- productId 变量将被设置为 789
2.2 多层路径变量
路径变量可以用在 URL 的多层路径中,提取差别层次的数据。
示例:- @GetMapping("/categories/{categoryId}/items/{itemId}")
- public String getItem(@PathVariable("categoryId") String categoryId,
- @PathVariable("itemId") String itemId) {
- return "Category ID: " + categoryId + ", Item ID: " + itemId;
- }
复制代码
- URL:/categories/1/items/42
- categoryId 变量将被设置为 1,itemId 变量将被设置为 42
3. 路径变量的名称匹配
- @PathVariable 的名称需要与 URL 模板中的占位符名称匹配。如果不匹配,会导致绑定失败。
示例:- @GetMapping("/items/{itemId}")
- public String getItem(@PathVariable("itemId") String id) {
- // 此处 id 是 itemId 的别名,实际名称应与 URL 模板一致
- return "Item ID: " + id;
- }
复制代码 4.2 @RequestParam
@RequestParam 是 Spring MVC 中用于处理 HTTP 请求参数的注解。它用于从 HTTP 请求的查询字符串或表单参数中提取数据。它可以用于处理 GET、POST、PUT、DELETE 等请求方法中的参数。
补充:
查询字符串 是附加在 URL 末端的键值对,用于向服务器通报参数。它通常用于 GET 请求,也可以用于其他请求类型。查询字符串以 ? 开始,后面跟随参数键值对,用 & 分隔多个参数。
查询字符串的格式为:- http://example.com/resource?key1=value1&key2=value2
复制代码 表单参数 是通过 HTML 表单提交的键值对,通常在 POST 请求中利用。表单参数可以通过 application/x-www-form-urlencoded 或 multipart/form-data 编码发送,具体取决于表单的内容类型。
表单数据的格式在 application/x-www-form-urlencoded 格式中,表单数据类似于查询字符串:示例
HTML 表单:- <form action="/register" method="post">
- <input type="text" name="username" value="alice">
- <input type="number" name="age" value="30">
- <button type="submit">Register</button>
- </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 的查询字符串中。
示例:- @RestController
- public class UserController {
- @GetMapping("/greet")
- public String greet(@RequestParam("name") String name) {
- return "Hello, " + name + "!";
- }
- }
复制代码
- 请求 URL:/greet?name=Alice
- 效果:Hello, Alice!
1.2 从表单数据中获取参数(POST 请求)
当利用 POST 请求时,参数通常是表单数据的一部分。
示例:- @RestController
- public class UserController {
- @PostMapping("/register")
- public String register(@RequestParam("username") String username,
- @RequestParam("password") String password) {
- return "Registered user: " + username;
- }
- }
复制代码
- 表单数据:
- <form action="/register" method="post">
- <input type="text" name="username" value="alice">
- <input type="number" name="age" value="30">
- <button type="submit">Register</button>
- </form>
复制代码 2. @RequestParam 的属性
2.1 value 属性
value(或 name)属性指定请求参数的名称。
示例:- @RequestParam(value = "name") String name
- // 或
- @RequestParam("name") String name
复制代码 2.2 required 属性
required 属性用于指示参数是否是必须的。默认为 true,如果请求中没有提供该参数,Spring 将抛出异常。如果设置为 false,则可以省略该参数。
示例:- @RequestParam(name = "name", required = false) String name
复制代码
- 如果请求中不包含 name 参数,name 将被设置为 null,而不是抛出异常。
2.3 defaultValue 属性
defaultValue 属性用于指定当请求参数缺失时的默认值。如果参数缺失,则利用默认值而不是抛出异常。
示例:- @RequestParam(name = "name", defaultValue = "Guest") String name
复制代码
- 如果请求中不包含 name 参数,name 将被设置为 "Guest"。
3. 路径变量与请求参数的区别
- 路径变量(Path Variables):用于从 URL 的路径中提取数据。例如:/users/{id}。
- 请求参数(Request Parameters):用于从查询字符串或表单数据中提取数据。例如:/users?id=1。
示例:- @RestController
- public class UserController {
- @GetMapping("/users/{id}")
- public String getUser(@PathVariable("id") String id) {
- return "User ID: " + id;
- }
- @GetMapping("/search")
- public String search(@RequestParam("query") String query) {
- return "Search query: " + query;
- }
- }
复制代码
- /users/1 利用路径变量,id 是路径的一部分。
- /search?query=spring 利用请求参数,query 是查询字符串的一部分。
4. 组合利用
@RequestParam 可以与其他注解(如 @PathVariable、@RequestBody)组合利用,处理复杂的请求。
示例:- @RestController
- public class UserController {
- @PostMapping("/users/{id}")
- public String updateUser(@PathVariable("id") String id,
- @RequestParam("name") String name,
- @RequestParam(name = "email", required = false) String email) {
- return "Updated user ID: " + id + ", Name: " + name + ", Email: " + (email != null ? email : "Not provided");
- }
- }
复制代码 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 利用 JSON 或 XML 来表示资源的状态。例如,获取用户资源可能会返回这样的 JSON 数据:- {
- "id": 1,
- "name": "Alice",
- "email": "alice@example.com"
- }
复制代码 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 格式。
示例:- @GetMapping("/user/{id}")
- public User getUser(@PathVariable Long id) {
- return new User(id, "Alice", 30); // 自动返回 JSON 数据
- }
复制代码 返回的 JSON 数据格式如下:- {
- "id": 1,
- "name": "Alice",
- "age": 30
- }
复制代码 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 获取请求的所有细节,包罗请求头、请求体和请求方法。
示例代码:
- @PostMapping("/process")
- public ResponseEntity<String> handleRequestEntity(RequestEntity<String> requestEntity) {
- // 获取请求方法
- HttpMethod method = requestEntity.getMethod();
-
- // 获取请求头
- HttpHeaders headers = requestEntity.getHeaders();
-
- // 获取请求体
- String body = requestEntity.getBody();
-
- // 打印请求信息
- System.out.println("Method: " + method);
- System.out.println("Headers: " + headers);
- System.out.println("Body: " + body);
-
- return ResponseEntity.ok("Request processed successfully");
- }
复制代码 请求示例:
- curl -X POST http://localhost:8080/process -H "Content-Type: text/plain" -d "Hello, Spring!"
复制代码 输出:
- Method: POST
- Headers: [Content-Type:"text/plain"]
- Body: Hello, Spring!
复制代码 在这个例子中,RequestEntity 提供了对请求的详细信息,便于处理复杂的请求逻辑。
构造 RequestEntity
RequestEntity 也可以通过静态工厂方法来创建,例如:- RequestEntity<String> requestEntity = RequestEntity
- .post(new URI("http://localhost:8080/process"))
- .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
- .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 示例
假设我们需要处理一个文件上传操作,并返回差别的响应状态码来表示上传的效果:
示例代码:
- @PostMapping("/upload")
- public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
- if (file.isEmpty()) {
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("文件为空");
- }
- try {
- // 假设保存文件到指定路径
- String uploadDir = "/path/to/upload/directory/";
- file.transferTo(new File(uploadDir + file.getOriginalFilename()));
- return ResponseEntity.ok("文件上传成功");
- } catch (IOException e) {
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件上传失败");
- }
- }
复制代码 请求示例:
- 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,例如:- ResponseEntity<String> response = ResponseEntity
- .status(HttpStatus.CREATED)
- .header(HttpHeaders.LOCATION, "/new-resource")
- .body("资源创建成功");
复制代码 这个例子返回了一个 201 Created 的响应,同时设置了 Location 响应头,指向新创建的资源路径。
ResponseEntity 的实用场景
- 需要灵活设置响应:当你需要自界说响应的状态码、头、体时,ResponseEntity 提供了很好的灵活性。
- RESTful API 响应:在 REST API 开发中,它是一个常见的选择,便于构建符合 HTTP 标准的响应。
6.4 RequestEntity 和 ResponseEntity 的关系
RequestEntity 主要用于处理 HTTP 请求,资助我们获取请求的所有详细信息;而 ResponseEntity 则用于构建和返回 HTTP 响应,资助我们返回自界说的响应数据、头和状态码。
常见组合利用场景
在 Spring MVC 的控制器方法中,你可以同时利用 RequestEntity 来获取请求信息,利用 ResponseEntity 返反响应。例如:- @PostMapping("/process-data")
- public ResponseEntity<String> handleRequest(RequestEntity<String> requestEntity) {
- // 获取请求体
- String requestBody = requestEntity.getBody();
-
- // 处理逻辑...
-
- // 返回响应
- return ResponseEntity.ok("处理成功:" + requestBody);
- }
复制代码 7、文件的上传和下载
在 Spring MVC 中,文件上传和下载是常见的需求,通常通过 MultipartFile 和 HttpServletResponse 等组件来处理。下面我们来详细讲解文件上传和下载的实现。
1. 文件上传
1.1 利用 MultipartFile 实现文件上传
Spring MVC 提供了 MultipartFile 接口来处理文件上传。为了支持文件上传,起首需要在项目的设置文件中启用对多部分请求的支持。
1.2 设置 Spring Boot 以支持文件上传
在 application.properties 或 application.yml 中设置最大文件上传巨细:- spring.servlet.multipart.max-file-size=10MB
- spring.servlet.multipart.max-request-size=15MB
复制代码
- max-file-size:限制单个文件的巨细。
- max-request-size:限制整个请求的巨细。
1.3 文件上传的 Controller 示例
- import org.springframework.web.bind.annotation.*;
- import org.springframework.web.multipart.MultipartFile;
- import org.springframework.http.ResponseEntity;
- import org.springframework.http.HttpStatus;
- import java.io.File;
- import java.io.IOException;
- @RestController
- @RequestMapping("/files")
- public class FileUploadController {
- @PostMapping("/upload")
- public ResponseEntity<String> uploadFile(@RequestParam("file") MultipartFile file) {
- // 检查文件是否为空
- if (file.isEmpty()) {
- return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("文件为空");
- }
- // 获取文件名
- String fileName = file.getOriginalFilename();
- try {
- // 文件保存路径(可以根据需求修改)
- String uploadDir = "/path/to/upload/directory/";
- File dest = new File(uploadDir + fileName);
- // 保存文件到服务器
- file.transferTo(dest);
- return ResponseEntity.ok("文件上传成功:" + fileName);
- } catch (IOException e) {
- e.printStackTrace();
- return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("文件上传失败");
- }
- }
- }
复制代码 1.4 前端文件上传表单示例
- <form method="POST" enctype="multipart/form-data" action="/files/upload">
- <input type="file" name="file"/>
- <button type="submit">上传文件</button>
- </form>
复制代码 在这个例子中,文件上传表单利用 enctype="multipart/form-data" 举行编码,使文件能被正确地传输到服务器。
2. 文件下载
文件下载通常通过 HttpServletResponse 来设置响应头并将文件的内容写入到输出流中。
2.1 文件下载的 Controller 示例
- import org.springframework.web.bind.annotation.*;
- import javax.servlet.http.HttpServletResponse;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.OutputStream;
- @RestController
- @RequestMapping("/files")
- public class FileDownloadController {
- @GetMapping("/download/{fileName}")
- public void downloadFile(@PathVariable("fileName") String fileName, HttpServletResponse response) {
- // 文件存储路径
- String filePath = "/path/to/upload/directory/" + fileName;
- File file = new File(filePath);
- if (!file.exists()) {
- response.setStatus(HttpServletResponse.SC_NOT_FOUND);
- return;
- }
- // 设置响应头
- response.setContentType("application/octet-stream");
- response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
- response.setContentLengthLong(file.length());
- try (FileInputStream fis = new FileInputStream(file);
- OutputStream os = response.getOutputStream()) {
- // 将文件数据写入输出流
- byte[] buffer = new byte[1024];
- int bytesRead;
- while ((bytesRead = fis.read(buffer)) != -1) {
- os.write(buffer, 0, bytesRead);
- }
- os.flush();
- } catch (IOException e) {
- response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- }
- }
- }
复制代码 2.2 前端文件下载链接示例
- <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 接口。
示例拦截器实现
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.web.servlet.HandlerInterceptor;
- import org.springframework.web.servlet.ModelAndView;
- public class MyInterceptor implements HandlerInterceptor {
- // 进入控制器之前
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- System.out.println("preHandle: 请求前检查权限...");
-
- // 可以进行权限验证逻辑,返回 false 则拦截请求
- String authToken = request.getHeader("Authorization");
- if (authToken == null || !authToken.equals("VALID_TOKEN")) {
- response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
- return false; // 拦截请求
- }
- return true; // 继续处理请求
- }
- // 控制器执行后,视图渲染前
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
- System.out.println("postHandle: 可以修改 ModelAndView...");
- }
- // 请求结束,视图渲染完成后
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- System.out.println("afterCompletion: 资源清理...");
- }
- }
复制代码 步骤二:注册拦截器
创建好拦截器类后,我们需要将其注册到 Spring 的拦截器链中。通常,我们通过设置类来注册拦截器。
示例设置
- import org.springframework.context.annotation.Configuration;
- import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
- import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
- @Configuration
- public class WebConfig implements WebMvcConfigurer {
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- // 注册拦截器
- registry.addInterceptor(new MyInterceptor())
- .addPathPatterns("/**") // 拦截所有路径
- .excludePathPatterns("/login", "/public/**"); // 排除某些路径
- }
- }
复制代码 在这个设置中,拦截器 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 方法在请求进入控制器之前拦截请求,举行权限验证或认证处理。如果验证失败,可以返回相应的错误响应,终止请求继续执行。
示例:
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- String authToken = request.getHeader("Authorization");
- if (authToken == null || !isValidToken(authToken)) {
- response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
- return false;
- }
- return true;
- }
复制代码 2. 日志记录
在 preHandle 或 afterCompletion 中记录请求的日志,包罗请求的 URL、参数、处理时间等。
示例:
- long startTime = System.currentTimeMillis();
- request.setAttribute("startTime", startTime);
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- long startTime = (Long) request.getAttribute("startTime");
- long endTime = System.currentTimeMillis();
- System.out.println("Request URL: " + request.getRequestURL() + " - Time Taken: " + (endTime - startTime) + "ms");
- }
复制代码 3. 数据预处理
在 preHandle 中举行数据的预处理,例如对请求参数举行格式化、数据补充等。
示例:
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- String param = request.getParameter("date");
- if (param != null) {
- // 预处理日期参数
- LocalDate date = LocalDate.parse(param, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
- request.setAttribute("formattedDate", date);
- }
- return true;
- }
复制代码 4. 数据后处理
在 postHandle 中对控制器返回的 ModelAndView 举行修改,添加公共的页面元素或数据。
示例:
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
- if (modelAndView != null) {
- modelAndView.addObject("footerMessage", "版权所有 © 2024");
- }
- }
复制代码 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 注解。
示例:- @RestController
- @CrossOrigin(origins = "http://localhost:8080")
- @RequestMapping("/api/users")
- public class UserController {
- // 控制器方法
- }
复制代码 或者可以在全局设置跨域:- @Configuration
- public class WebConfig implements WebMvcConfigurer {
- @Override
- public void addCorsMappings(CorsRegistry registry) {
- registry.addMapping("/**")
- .allowedOrigins("http://localhost:8080")
- .allowedMethods("GET", "POST", "PUT", "DELETE");
- }
- }
复制代码 8、常见开发模式
在前后端分离中,Spring MVC 通常与 RESTful API 模式联合利用,前端通过 HTTP 方法调用后端的 API 接口。后端仅负责数据处理和业务逻辑,而前端负责用户界面、数据展示和交互。
- 前端利用 Axios 或 Fetch 调用后端 API:
前端通过 AJAX 向后端发送请求:- axios.get('/api/users/1')
- .then(response => {
- console.log(response.data);
- })
- .catch(error => {
- console.error(error);
- });
复制代码 - 后端返回 JSON 数据:
后端 Controller 处理请求并返回 JSON 数据,前端接收数据后举行页面更新。
9、总结
在前后端分离模式下,Spring MVC 的主要职责是处理请求、举行业务逻辑处理、返回 JSON 或 XML 格式的数据。其核心组件如 @RestController、@RequestMapping、@ResponseBody 等,简化了 RESTful API 的开发流程。此外,Spring MVC 还提供了强大的异常处理机制和跨域支持,使前后端分离的开发更加顺畅。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |