带你进入 Spring MVC 的乐园
1. 什么是 Spring MVC ?
Spring Web MVC
是基于 Servlet API
构建的原始 Web
框架,从⼀开始就包含在 Spring
框架中。它的正式名称“Spring Web MVC”
来自其源模块的名称(Spring-webmvc),但它通常被称为“Spring MVC”
(这是官方描述的)
1.1 MVC 的定义
要想知道 Spring MVC
是什么,我们得先知道 MVC
的定义。MVC
的全称是Model View Controller
,它将应用程序分为三个核心部分:模型(Model)、视图(View)和控制器(Controller)。
- 模型(Model)是应用程序中用于处理数据和业务逻辑的部分,它代表了应用程序的核心,通常用于管理应用程序的状态。
- 视图(View)是应用程序中的用户界面,它展示了模型的内容,以便用户能够与应用程序进行交互。
- 控制器(Controller)是应用程序中用于协调模型和视图之间的交互的部分,它从视图中接收输入,处理用户的请求,并在需要时更新模型,以便呈现正确的视图。
1.2 Spring MVC 与 MVC 的关系
MVC
是⼀种思想,而 Spring MVC
是对 MVC
思想的具体实现。Spring MVC
是基于MVC
模式的Web
框架,它在MVC
架构基础上进行了扩展和改进。
Spring MVC
提供了一组强大的工具和组件,用于支持Web
应用程序的开发和维护,同时它也遵循MVC
模式的核心思想,即将应用程序划分为模型、视图和控制器三个主要组件。
2. Spring MVC 的创建和连接
2.1 创建 Spring MVC 项目
Spring MVC
项⽬创建和 Spring Boot
创建项⽬相同(Spring MVC 使⽤ Spring Boot 的⽅式创建),在创建的时候选择 Spring Web
就相当于创建了 Spring MVC
的项⽬。
如上图,勾选后就相当于创建了 Spring MVC
的项⽬。(注意:Spring Boot version 的版本是2.x)
2.2 @RequestMapping 注解
@RequestMapping
注解可以被应用在控制器类上,也可以被应用在控制器中的方法上。在控制器类上使用@RequestMapping
注解可以定义该类处理的所有请求的前缀URL,而在方法上使用@RequestMapping
注解则可以定义该方法处理的具体URL
,当修饰类和方法时,访问的地址是类 + 方法
。
@Controller //将类存入 spring 容器
@ResponseBody //定义返回的数据格式为⾮视图(text/html)
@RequestMapping("/user")// 路由器规则注册
public class UserController {
// 路由器规则注册
@RequestMapping("/hi")
public String hi(){
return "hello world";
}
}
2.3 @RequestMapping 注解 是 post 还是 get 请求?
我们知道通过网页向服务器发送消息都是以一个请求来发送的,请求的种类有很多,比如:
- GET请求:用于获取指定资源的信息,通常用于查询或读取操作。
- POST请求:用于向服务器提交数据,通常用于创建新的资源或执行状态更改操作。
- PUT请求:用于更新指定资源的信息,通常用于更新已有的资源。
- DELETE请求:用于删除指定资源,通常用于删除不再需要的资源。
- PATCH请求:用于更新资源的部分信息,通常用于局部更新资源的信息。
……
那这里的 @RequestMapping
可以接收什么请求呢?这里需要借助PostMan
工具:
GET请求:成功
POST请求:成功
PUT请求:成功
@RequestMapping
注解默认的情况支持所有类型的 HTTP 请求,后面的请求就不举例了。
2.4 指定接收类型
指定@RequestMapping
接收POST
请求:两种写法
@RequestMapping(value = "/hi",method = RequestMethod.POST)
@PostMapping("/hi")
@RestController //@RestController = @Controller + @ResponseBody
@RequestMapping("/user")// 路由器规则注册
public class UserController {
// 仅支持 Post 请求
@PostMapping("/hi")
public String hi(){
return "hello world";
}
}
Post
请求可以被访问。
指定接收类型的注解:
注解 | 用途 | 示例 |
---|---|---|
@RequestMapping | 将 HTTP 请求映射到特定的处理方法上 | @RequestMapping(value = "/users/{id}", method = RequestMethod.GET) 这里可以设置其它请求。 |
@GetMapping | 将 HTTP GET 请求映射到特定的处理方法上 | @GetMapping("/users/{id}") |
@PostMapping | 将 HTTP POST 请求映射到特定的处理方法上 | @PostMapping("/users") |
@PutMapping | 将 HTTP PUT 请求映射到特定的处理方法上 | @PutMapping("/users/{id}") |
@DeleteMapping | 将 HTTP DELETE 请求映射到特定的处理方法上 | @DeleteMapping("/users/{id}") |
@PatchMapping | 将 HTTP PATCH 请求映射到特定的处理方法上 | @PatchMapping("/users/{id}") |
3. 获取参数
3.1 传递参数
直接用方法中的形参来接收参数:
//用形参接受参数
@GetMapping("/accept")
public String accept(Integer id,String name){
//返回 id 和 name
return "id = " + id + " name = " + name;
}
query string
的键值对中的键必须要与形参相同,如果不同就接收不到数据:
当有多个参数时,前后端进行参数匹配时,是以参数的名称进行匹配的,因此参数的前后顺序是不影响后端获取参数的结果。
3.2 传递对象
传递对象的时候需要后端定义一个相应的对象类,必须添加它的get、set方法,这里传一个学生类为例:
public class Student {
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id + ", name=" + name + ", age=" + age +'}';
}
}
@RequestMapping("/accept1")
public Student accept1(Student student){
//返回一个对象
return student;
}
方法中的 Student
类型参数表示这个方法将会接收一个名为 student
的请求参数,Spring MVC 框架将自动将 HTTP 请求中的参数绑定到该对象上,并将其作为方法的入参传入。
方法的返回值类型为 Student
类型,表示该方法将会返回一个 Student
类型的对象。返回值会自动被 Spring MVC 框架转换为 JSON 格式并发送给客户端。
可以返回一个字符串:
@RequestMapping("/accept2")
public String accept2(Student student){
//回应:一个对象
return student.toString();
}
3.3 后端参数重命名
前端传递的参数 key
和我们后端接收的 key
可以不⼀致,这里就要用到 @RequestParam
注解。
@RestController //@RestController = @Controller + @ResponseBody
@RequestMapping("/user")// 路由器规则注册
public class UserController {
@GetMapping("/accept")
public String accept(@RequestParam("myID") String id,String name){
//返回 id 和 name
return "myID = " + id + " name = " + name;
}
}
但是重命名后这个属性不能省略,不然报错:
如何解决这个问题呢?如下:添加required = false
//用形参接受参数,并对它重命名
@GetMapping("/accept") //只有一个参数的时候 name 可以省略
public String accept(@RequestParam(name = "myID",required = false) String id,String name){
//返回 id 和 name
return "myID = " + id + " name = " + name;
}
这时候如果省略不会报错,为null
。
3.4 @RequestBody 接收 JSON 对象
接收 JSON
对象需要用到注解@RequestBody
:
@RestController //@RestController = @Controller + @ResponseBody
@RequestMapping("/user")// 路由器规则注册
public class UserController {
//接收一个 JSON 对象
@RequestMapping("/accept4")
public Student accept4(@RequestBody Student student){
return student;
}
}
前端如何传一个JSON
对象呢?这里继续用Postman
工具:
如果加了@RequestBody
注解而不传json
对象会报错。
3.5 从 path 中获取参数
我们可以从url
的path
部分获取参数。
在前面,url
传参的方式是键值对的形式:user/accept4?id=1&name=小明&age=10
;其实还有另一种传参方式,那就是通过路径:/user/accept4/1/小明/10
,这时候后端就需要知道哪一个路径是哪一个属性,并且还要借助@PathVariable
注解:
//同过路径接收参数
@RequestMapping("/accept5/{id}/{name}/{age}") //定义属性
public String accept5(@PathVariable Integer id,@PathVariable String name,@PathVariable Integer age){
return "id: " + id +" name: " + name +" age: " + age;
}
@PathVariable
同样可以设置required = false
、name=""
,它们的作用这里不再赘述。
3.6 上传文件 @RequestPart
后端接收文件:
@RequestMapping("/accept6")
public boolean accept6(@RequestPart("myimg")MultipartFile file) throws IOException {
File saveFile = new File("D:\\picture\\图片.png");//文件存放的路径
try{
file.transferTo(saveFile);//将接收的文件放在上面的路径中
return true;
}catch (IOException e){
e.printStackTrace();
}
return false;
}
file.transferTo(saveFile)
将上传的文件内容传输到指定的 saveFile
文件中。
@RequestPart注解
里的"myimg"
是与前端form
表单中的name
属性相对应,但是这里传文件就不用form
表单了,继续用Postman
。
但是文件超出了1MB就会报错,我们需要在配置文件中配置最大上传大小:
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
multipart.max-file-size
指定了单个文件上传的最大大小。如果上传的文件大小超过了这个限制,服务器将返回一个错误响应。multipart.max-request-size
指定了整个请求的最大大小,包括所有文件和其他表单字段的数据。如果请求的总大小超过了这个限制,服务器将拒绝接收该请求。
这两个属性都接受带有单位的值,如字节(B)、千字节(KB)、兆字节(MB)、吉字节(GB)等。常用的单位包括B、KB、MB和GB。
3.6.1 避免重复文件名的 UUID
上面的上传文件案例中,有一个问题,就是在后端程序中文件名是定死了的,不能改变,这就导致上传的文件总是被覆盖,解决问题的办法就是使用UUID。
UUID的目的是确保在不同的计算机和网络环境中生成的标识符都是唯一的。UUID的生成算法基于计算机的时间戳、网络地址、随机数等信息,以确保生成的标识符足够唯一。
@RequestMapping("/accept6")
public boolean accept6(@RequestPart("myimg")MultipartFile file) throws IOException {
//获取文件的后缀名
String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
//文件名字
String fileName = UUID.randomUUID() + suffix;
File saveFile = new File("D:\\picture\\" + fileName);
try{
file.transferTo(saveFile);
return true;
}catch (IOException e){
e.printStackTrace();
}
return false;
}
再次上传:
我多上传几次:
3.7 获取Cookie、Session、header
3.7.1 获取 Cookie、header (方法一)
@RequestMapping("/accept7")
public String accept7(HttpServletRequest request, HttpServletResponse response){
//获取请求参数的值
String name = request.getParameter("name");
//获取所有 Cookie
Cookie[] cookies = request.getCookies();
//获取 HTTP 的请求头
String userAgent = request.getHeader("User-Agent");
return name + ": " + userAgent;
}
3.7.2 用注解获取 Cookie、header (方法二)
Cookie:
@RequestMapping("/accept8")
public String accept8(@CookieValue(name = "cookie",required = false) String cookie){
return "cookie: " + cookie;
}
可以造一个cookie:
header:
@RequestMapping("/accept9")
public String accept9(@RequestHeader(name = "User-Agent",required = false) String userAgent) {
return "userAgent:"+userAgent;
}
3.7.3 Session 存储和获取
//设置 session
@RequestMapping("/setSession")
public void accept10(HttpServletRequest request) {
HttpSession session = request.getSession(true);
if(session != null){
session.setAttribute("1","张三");
}
}
//获取 session
@RequestMapping("/getSession")
public String getSession(HttpServletRequest request){
HttpSession session = request.getSession(false);
String name = null;
if(session != null && session.getAttribute("1")!=null){
name = (String) session.getAttribute("1");
}
return "name: " + name;
}
先对setSession
请求:
再对getSession
请求:
可以用注解@SessionAttribute
来获取session
:
//获取 session
@RequestMapping("/getSession2")
public String getSession2(@SessionAttribute(name = "1",required = false) String name){
return "name: " + name;
}
4. 返回数据
4.1 返回静态页面
在项目的static
下创建一个静态页面。
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping("/getHtml")
public Object getHtml(){
return "/test.html"; //这里一定要加“/”
}
}
这里不能加@ResponseBody
。
通过在路径前添加斜杠 /
,可以确保路径被解析为绝对路径,即从应用程序的根路径开始查找资源。这样做可以避免路径的相对性,并且不受请求路径的影响。
假设应用程序的根路径是 http://localhost:8080
,假设当前请求的路径是 http://localhost:8080/user/test
,如果使用相对路径返回静态页面,return "test.html"
将被解析为相对于当前请求的路径,即 http://localhost:8080/user/test/test.html
。
而如果使用绝对路径,return "/test.html"
将被解析为 http://localhost:8080/test.html
4.2 请求转发与请求重定向
return 不但可以返回⼀个视图,还可以实现跳转,跳转的⽅式有两种:
- forward 请求转发
- redirect 请求重定向
它们的区别:
-
请求转发:
- 在请求转发中,服务器直接将请求转发给另一个资源进行处理,而客户端浏览器并不知道这个跳转的过程。
- 请求转发是服务器内部的操作,只有一个请求和一个响应,浏览器地址栏的URL不会改变。
- 请求转发可以将请求的数据(请求参数、属性等)在转发的过程中保持不变,可以共享同一个请求和会话对象。
- 使用请求转发时,可以直接访问转发目标的resources目录下的资源,因为请求仍然在服务器内部进行。
// 请求转发
@RequestMapping("/index2")
public String index2(){
return "forward:/demo1.html";
}
-
请求重定向:
- 在请求重定向中,服务器返回一个特殊的响应给浏览器,告诉浏览器需要发起一个新的请求去访问另一个资源。
- 请求重定向是客户端行为,浏览器会收到服务器返回的重定向响应,然后自动发起一个新的请求去访问指定的URL。
- 请求重定向会导致浏览器地址栏的URL发生改变,显示的是重定向的目标URL。
- 请求重定向时,请求的数据不会保持,因为是两个独立的请求,所以请求参数、属性等不会共享。
// 请求重定向
@RequestMapping("/index")
public String index(){
return "redirect:/demo1.html";
}
@RequestMapping("/index3")
public void index3(HttpServletResponse response) throws IOException {
//请求重定向
response.sendRedirect("https://juejin.cn/");
}
转载自:https://juejin.cn/post/7233384764564357179