目录
前言:
MVC 是什么?
1. Spring MVC 项目的创建和连接:
1.1 创建
1.2 连接
2. @RequestMapping 注解使用详析:
2.1 指定请求类型:
2.1.1 指定 GET 请求
2.1.2 指定 POST 请求
3. 参数的获取与传递:
3.1 传递单个参数
3.2 传递对象
3.3 传递多个参数/表单传参(非对象)
3.4 后端参数重命名:
非必传参数的设置
3.5 @RequestBody 接收 JSON 对象
3.6 获取 URL 中的基础参数
3.7 上传文件
3.8 获取 Cookie/Header/Session:
3.8.1 获取 Cookie
3.8.2 获取 Header
3.8.3 Session 的存储和获取
3.9 获取静态页面
3.10 请求转发 和 请求重定向:
二者的区别
前言:
Spring MVC 原名“Spring Web MVC”,是基于 Servlet API 构建的原始 Web 框架。
MVC 是什么?
MVC 即 Model View Controller,是一种软件架构模式,可以把软件系统分为模型、视图、控制器三个部分;
- Model (模型):用于处理应用程序数据逻辑的部分;
- View(视图):应用程序中处理数据显示的部分;
- Controller(控制器):应用程序中用于处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据。
因此可以认为:Spring MVC 是实现了一个 MVC 模式,并继承了 Servlet API 的 Web 框架。同时 Spring MVC 项目就可以感知到用户在浏览器 URL 里输入的请求。
学习 Spring MVC 要掌握其主要的三个功能:
- 连接的功能:将浏览器和 java 程序连接起来,通过访问一个地址就能调用到对应的 Spring 程序;
- 获取参数的功能:在程序中获取用户访问传递的参数;
- 输出数据的功能:将执行结果返回给用户;
1. Spring MVC 项目的创建和连接:
1.1 创建:
Spring MVC 项目的创建和 Spring boot 项目创建步骤一样,只需要添加依赖时加上 “Spring Web” 即可。
1.2 连接:
连接既是路由映射,在 Spring MVC 中使用 @RequestMapping 注解来实现与浏览器 URL 的路由映射。访问时直接在浏览器 URL 中访问对于路径即可。
2. @RequestMapping 注解使用详析:
以上只是简单了解了一下使用该注解建立连接,下面剖析该注解的具体使用规则;
@RequestMapping 用来注册接口路由映射,当用户访问一个 URL 时,将用户的请求对应到程序中某个类的某个方法中。
- @RequestMapping 注解可以修饰 [类和方法] ,也可以直接修饰[方法] 。
- @RequestMapping 可以指定使用 GET/POST 请求;
2.1 指定请求类型:
2.1.1 指定 GET 请求:
普通的 @RequestMapping 默认就是发送的 GET 请求;
△ GET请求的三种写法:
// GET 请求的三种写法:
// 第一种:
@RequestMapping("/request")
// 第二种:
@RequestMapping(value = "/request", method = RequestMethod.GET)
// 第三种:
@GetMapping("/request")
2.1.2 指定 POST 请求:
△ POST 请求的两种写法:
// POST 请求两种写法:
// 第一种:
@RequestMapping(value = "/request", method = RequestMethod.POST)
// 第二种:
@PostMapping("/request")
3. 参数的获取与传递:
3.1 传递单个参数:
在 Spring MVC 中可以直接使用方法中的参数进行传参;
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
// 传递参数
@RequestMapping("/send")
public Object sendParam(String username) {
return "username: " + username;
}
}
在 postman 中构造请求:
3.2 传递对象:
Spring MVC 可以自动将传递的参数赋值给对象;
@Component
@Data
public class Student {
private int id;
private String name;
private int age;
}
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
// 传递对象
@RequestMapping("/send")
public Object sendObject(Student student) {
return "student 对象:" + student;
}
}
在 postman 中构造请求:
3.3 传递多个参数/表单传参(非对象):
当有多个参数时,前后端是以参数名来进行参数匹配的,因此参数的位置不会影响后端获取参数的结果。
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
@RequestMapping("/send")
public Object sendManyParas(String name, String pwd) {
String user = name + " " + pwd;
return user;
}
}
3.4 后端参数重命名:
当前端给后端传递的参数与后端代码中设置的参数名不一样时,后端就可以通过参数重命名-参数映射来匹配。
使用 @RequestParam 注解来重命名参数:
这个例子中,前端传递的参数为 uid,但后端设置与之对应的重命名参数为:username;
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
@RequestMapping("/send")
// 参数重命名
public Object resetPara(@RequestParam(value = "uid") String username) {
return "uid : " + username;
}
}
非必传参数的设置:
反过来想:既然后端的参数名为:username,那前端传一个 username 可不可以呢?
报错 400 了,可见是不行的。因为 @RequestParam 注解重命名意为:后端规定前端必须传一个“uid”的参数,与后端的 username 匹配,进入该注解内部可以看到:
所以这里报错就是因为我们在没有设置 required 的情况下传了个 username,不是后端规定的“uid”。那么可以通过设置 required 的值来提高前端传参的灵活性,让“uid”变成非必须传递的参数:
注意:这样只是设置非必须传递的参数,并不代表传的参数可以被匹配到;
3.5 @RequestBody 接收 JSON 对象:
后端接收 Json 对象:
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
@RequestMapping(value = "/send", method = RequestMethod.POST)
public Object sendJson(@RequestBody Student student) {
return "Json 对象:" + student;
}
}
前端构造 Json 对象:
如果去掉 @RequestBody,可以拿到 Json 对象但无法接收到Json对象里属性的值:
3.6 获取 URL 中的基础参数:
使用 @PathVariable 来获取 URL 里 ? 之前的参数:
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
// 获取 URL 键值参数
@PostMapping("/send/{name}/{password}")
public Object getURLKV(@PathVariable String name, @PathVariable String password) {
return "name:" + name + " password:" + password;
}
}
注意:
- @RequestParam:获取 URL 里 ? 之后的参数中的参数;
- @PathVariable:获取 URL 里 ? 之前基础部分的参数;
3.7 上传文件:
上传文件使用 @RequestPart 注解;
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
@RequestMapping("/upload")
public Object uploadFile(@RequestPart("myfile")MultipartFile file) {
// 获取文件名
String fileName = UUID.randomUUID() +
file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
// 文件保存路径
File savaFile = new File("D:\\MVCupload\\" + fileName);
// 保存文件
try {
file.transferTo(savaFile);
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}
获取文件名时借助 UUID 和 substring 保证多次上传同一个文件不会导致同名覆盖;
如果担心上传的文件超过了限制大小,可以在配置文件中设置文件可上传最大量:
3.8 获取 Cookie/Header/Session:
3.8.1 获取 Cookie:
使用 @CookieValue 注解来获取 cookie:
这是用来获取单个 Cookie 的方法:
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
@RequestMapping("/cookie")
public Object getCookie(@CookieValue(value = "load", required = false) String load) {
return "load:" + load;
}
}
如果要获取所有的 Cookie,就可以使用传统获取方法,借助 request 对象获取:
@RequestMapping("/cookie")
public Object getCookies(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cookies = request.getCookies();
return cookies;
}
3.8.2 获取 Header:
使用 @RequestHeader 注解获取 Header 中的信息:
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
@RequestMapping("/header")
public Object getHeader(@RequestHeader("User-Agent") String userAgent) {
return "User-Agent:" + userAgent;
}
}
3.8.3 Session 的存储和获取:
先按照 HttpServletRequest 存储 Session,再通过 @SessionAttribute 注解获取 Session:
@Controller
@ResponseBody
@RequestMapping("/user")
public class UserController {
// 存储 Session
@RequestMapping("/setSession")
public void setSession(HttpServletRequest request) {
HttpSession session = request.getSession();
session.setAttribute(SESSION_KEY,"张三");
}
private static final String SESSION_KEY = "USER_SESSION";
// 获取 Session
@RequestMapping("/getSession")
public Object getSession(@SessionAttribute(SESSION_KEY) String name) {
return "session_name:" + name;
}
}
更简洁的方式获取 session:
@RequestMapping("/session")
public Object getSession(@SessionAttribute(value = "session", required = false) String session) {
return session;
}
3.9 获取静态页面:
首先在 static 目录下创建一个 index.html 文件:
获取这个静态页面时,一定要注意是 return "/index.html",这个 / 斜杠的作用是从根目录找 index.html 这个静态页面,因为其直接被放在 static 目录下;
如果这个文件被放在 static 目录下的 index 目录下,就要这样写:return "index.html" ,同时 URL 中路径为:http://localhost:8080/index/index.html
@RequestMapping("/test")
public class TestController {
@RequestMapping("/index")
public Object getIndex() {
return "/index.html";
}
}
3.10 请求转发 和 请求重定向:
二者的区别:
- 定义不同:
请求转发(Forward):发生在服务端程序内部,当服务端收到客户端发来的请求时,会将请求转发给最终的目的地址,再将目的地址的结果返回给客户端。
请求重定向(Redirect):服务端接收到客户端的请求之后,会给客户端换返回一个临时响应头,这个临时响应头中记录着客户端需要再次发送请求的 URL 地址,客户端再收到这个新的地址后,就重新向新的地址发送请求。
形象举例:老大找老二借钱,老二现在没钱,但是老二去找老三借到钱后给老大,这个过程就是“请求转发”,如果老二只是让老大去找老三借钱,这个过程就是“请求重定向”。- 请求方不同:
请求转发主要是服务端在完成任务,而请求重定向主要是客户端和服务端在不断交互完成任务。- 数据共享不同:
请求转发的整个执行流程中,服务端带着同一个Request 和 Response 对象完成了所有的交互,所以请求和返回的数据是共享的;而请求重定向每次的请求和返回数据是不同意的。- 最终 URL 地址不同:
请求转发是服务端作为代理请求去找目的地址,最终将结果再带回来,所以最终的 URL 地址不变;而请求重定向在每次交互中 URL 都在发生改变。- 实现代码不同,具体代码如下。
@Controller
@RequestMapping("/test")
public class TestController {
// 请求转发
@RequestMapping("/forward")
public Object forward() {
return "forward:/index.html";
}
}
请求转发的最终地址还是原请求的地址:
@Controller
@RequestMapping("/test")
public class TestController {
// 请求重定向
@RequestMapping("/redirect")
public Object redirect() {
return "redirect:/index.html";
}
}
请求重定向最终的地址是重定向的地址:
需要注意的:请求转发时,如果资源和转发的页面不在同一个目录下,会导致外部资源不可访问,但 redirect 可以访问