目录
一、介绍 MVC
二、Spring MVC 的三个基本功能
1.1 连接功能
1.2 Spring MVC 的创建和使用
1.3 @RequestMappig 介绍
1.4 Spring MVC 实现用户和 Spring 程序的连接
1.5 @GetMapping 和 @ PostMaping 注解
1.6 Get 和 Post请求注解的多种写法
2.1 获取请求中参数的功能
2.2 获取7种类型的参数
(1)获取一个参数
(2)获取多个参数
(3)获取一个对象
(4)获取一个 json 对象
(5)获取 URL中携带的参数
(6)上传一个文件
(7)参数重命名
1. 参数少传递:
2. 参数传递名称不一样:
3. 参数传递的顺序不同:
(8)Cookie 和 Session 以及 Header 的获取
前言
Spring Web MVC 通常被称为 Spring MVC,是基于 Servlet API 构建的一个 Web 框架,前文介绍过的 Spring 项目的使用还不够,Spring 只是一个 IoC容器,将各种方法和类托管给 Spring,之后就需要和前端建立连接,所以 Spring MVC 可以实现和前端的交互,Spring MVC 就是 Spring 框架的核心模块,而 Spring Boot 是 Spring 框架的脚手架,是为了快速开发 Spring 项目的,这就是三者的区别。
MVC 就是三个模块的缩写:Model(数据库模块),View(视图,也就是前端),Conller(控制器,校验前端请求的数据并向后端数据库发送数据)。
MVC是一种模式,而 Spring MVC 是这种模式的具体实现,Spring MVC 实现了这种思想,并且基于 Servlet API 构建,形成的一个 Web 框架,有了Spring MVC 我们就可以和前端建立连接,然后基于 Spring MVC,实现一些业务逻辑。
将浏览器和 Java 程序连接起来,当用户访问一个地址时,Spring MVC 可以帮我们实现 URL 和 Spring 中的业务代码连接起来
首先创建一个 Spring 项目:
然后添加起步依赖:(在添加完 devTools 外部 jar 包之后就已经是 Spring MVC 项目了)
最后点击 Finish 即可。
@RequestMapping 注解也是开发中很常用的注解,可以实现路由注册,所谓路由注册就是用户在浏览器中输入了一个 URL 之后,可以将用户的请求对应到 Spring MVC 项目中的某个类的某个方法,这个映射的过程就是 路由注册。
建立 UserController 类,写入 sayHi()方法,加上 @RequestMapping 和 @RestController 注解,如下代码:
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController2 {
@RequestMapping("/user/sayHi")
public String sayHi() {
return "do sayHi()";
}
}
然后在程序启动类中启动项目,记住日志信息中的端口号,最后在浏览器中访问 路由映射的 url:
注:可以看到上述代码是将 @RequestMapping 注解中的参数 设置了两个层级的路由,这是一种方式,还可以在类上加 @RequestMapping 注解然后设置 一个类的 路由地址,如果在一个类上设置了 @RequestMapping 注解,url 的格式就是 类 + 方法 的路由地址。
在网络协议中介绍过,请求的方式有很多种,有get,post,put,delete 方法,而常用的请求方法就是 get 和 post 方法,那此时 @RequestMapping属于什么方式呢。可以使用 fiddle 抓包工具看一下:
上图所示:请求的方法是 Get 方法,那如果是 Post 方法是否可行。可以使用 postman 模拟请求工具试一下,下图所示:
注:在浏览器中输入的url 默认就是一个 Get 方法,所以要使用 Post 方式构造 http 请求就需要借助工具。
上图所示,@RequestMapping 注解可以实现两种 http 请求 方式,但是如果某种业务需求下如果只能使用 Get 或者 Post 一种请求方式,此时就需要使用@GetMapping 和 @ PostMaping 注解或者给 @RequestMapping 加一个参数 来限制用户请求只能使用一种方式构造 http 请求:
重启项目,再去访问 sayHi()方法之后:可以看到报405状态码,显示 get 方法请求不能访问:
Get请求的三种使用注解写法:
@RequestMapping(value = "/user/sayHi", method = RequestMethod.GET)
@GetMapping("/user/sayHi")
@RequestMapping("/user/sayHi")
Post请求的两种使用注解写法:
@RequestMapping("/user/sayHi")
@RequestMapping(value = "user/sayHi", method = RequestMethod.POST)
使用上述请求方法的写法可以达到同样的效果。
当用户输入 URL后可以在 URL 中,也可以是在 http 请求中携带数据,这部分数据就是用户传过来的参数,Spring MVC 可以帮我们获取到这些各种各样的参数
(1)获取一个参数 |
(2)获取一个表单参数 / 获取多个参数 |
(3)获取一个普通对象 |
(4)获取一个 json 格式的对象 |
(5)获取 URL 中携带的参数 |
(6)上传一个文件 |
(7)Session 的存储和获取 |
package com.example.demo.controller;
import org.springframework.web.bind.annotation.*;
@RestController
public class UserController2 {
@RequestMapping("/user/sayHi")
public String sayHi(String name) {
return "do sayHi() --> " + name;
}
}
注:此时在 URL 中传递的参数必须和 传递的形参名保持一致,否则无法获取到这个参数。
浏览器访问 url 中加上 name 参数:
@RestController
public class UserController2 {
@RequestMapping("/user/sayHi")
public String sayHi(String name, String password) {
return "do sayHi() --> " + name + " " + password;
}
}
浏览器访问 url 中加上 name 和 passpord 参数:
获取一个表单参数和获取多个参数是同样的写法,此处不再介绍。
获取一个对象,首先在程序中要有一个实体类来作为数据传输的载体:
package com.example.demo.entity;
import lombok.Data;
@Data
public class Userinfo {
private int id;
private String name;
private String password;
private int age;
}
之后在 Controller 层写入获取对象的方法:
@RequestMapping("/reg")
public Object reg(Userinfo userinfo) {
System.out.println(userinfo);
return userinfo;
}
之后在浏览器中传入响应的参数即可 (注:此时不需要传递一个真正的对象,只需要把响应的参数写对,之后 Spirng MVC 框架就会帮我们自动实现 参数 和 对象属性 之间的映射,而且参数在返回的时候也是不需要指定返回的类型,因为 Object 类是所有类的父类,此时 框架在数据返回的时候也会帮我们进行 数据类型的判断,最后返回一个合适的类型)
我们可以写另一个方法来验证上述注意中的结论是否正确:
在 Controller 层中写入方法:
@RequestMapping("/h1")
public Object getH1() {
return "我是h1标签
";
}
可以看到上述代码中的 geth1 方法的返回类型是一个 Object 类型,但是通过下图中的抓包可以看到 响应的数据正文中的类型已经是 html 格式的类型了,所以不用设置 响应的数据类型,这些事情 Spring MVC 框架已经帮我们实现好了。
注:如果前端传递的数据是一个json 对象,此时用一个 普通的对象来接收这个 json 对象是不可以拿到参数的。
前端传递一个对象有两种方式:1. 构造一个 Ajax 请求来封装一个 json 对象。 2. 使用 postman 模拟请求工具构造一个带有 json 格式的对象 的请求。
第二种方式是最简单的构造一个 json 对象,所以介绍下用 postman 来传递一个 json 对象:
先在 Controller 层中写入 方法,此时用一个普通的对象这种形参是拿不到 json 格式的对象的;需要加一个 @RequestBody 注解来实现接收一个 json 对象 。
@RequestBody 注解也很好理解:就是在一个请求正文中获取一个对象。
//传递一个json 对象
//使用 @RequestBody 来获取
@RequestMapping("/user/reg")
public Object getJson(@RequestBody Userinfo userinfo) {
System.out.println(userinfo);
return userinfo;
}
用 postman 构造一个带有 json 对象的请求:
用 fiddle 抓包工具抓以下看一下前端传递的是否是一个 json 对象:
注:此处的获取 URL 中的参数不是从 URL 参数部分获取参数,而是就在一个 URL 中获取参数。
获取 URL 中的参数需要使用注解 @PathVariable,就是从路径中获取一个变量的 意思
先在Controller 层中写入方法:
// 获取一个 URL 中的参数
@RequestMapping("/reg2/{name}/{password}")
public Object reg2(@PathVariable String name,@PathVariable String password) {
return "name --> " + name + " password --> " + password;
}
之后在浏览器输入 URL:
注:一种参数是 query string 的形式,在 ” ?“ 之后的参数,一种是在 url 中的参数,第一种方式的参数更加简洁,而且浏览器抓取关键字优先级更高(就是在浏览器中会在页面种优先显示),第二种 query string 的格式更适合传递多个参数。
所以,如果是很少的参数,推荐使用第一种方式,如果参数很多,不推荐使用第一种方式。
上传一个文件也需要使用注解:@RequestPart
//上传一个文件
@RequestMapping("/myUpload")
public Object upload(@RequestPart("myImg")MultipartFile file) {
File saveFile = new File("D:\\home\\myImg.png");
try {
file.transferTo(saveFile);
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
使用 postman 模拟一个上传文件的请求:
如果上传的文件不是图片或者有下沟通的文件名要上传两份,上述代码的写法就不合适了,就需要生成不同的文件名,最主要的就是文件名后缀,上传的文件可以是各种各样的文件,所以后缀名不能是固定的,应该是如下代码:
(UUID中的 randomUUID() 方法可以生成一个唯一的文件名)
(在文件IO中有 getOriginalFilename() 方法,就是获取原始的文件名,这个文件名中就包含了文件后缀名,然后通过字符串截取的方式来获取文件后缀名)
//上传一个文件
@RequestMapping("/myUpload")
public Object upload(@RequestPart("myImg")MultipartFile file) {
String fileName = UUID.randomUUID() + //文件名
file.getOriginalFilename().substring(// 文件名后缀
file.getOriginalFilename().lastIndexOf("."));
File saveFile = new File("D:\\home\\myImg" + fileName);
try {
file.transferTo(saveFile);
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
注:需要注意上传文件大小是有限制的,单次上传文件大小不能超过 1 MB,否则会报异常;如果上传的文件大小超过 1MB,此时可以在配置文件中设置 最大上传的文件大小;如下代码:
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB
上述几种传递参数的方式如果前端的参数没有传递过来 或者 传递的形参名称不一样 以及 参数的顺序不一致 会怎么样呢?
例如:在传递多个参数时,可以看下图的执行结果:
解决方法:加注解 @RequestParam 来实现后端参数的 映射 并且 设置 参数是否必须传递。
如下代码:
//传递多个参数
@RequestMapping("/user/sayHi")
public String sayHi(@RequestParam String name,@RequestParam String password) {
return "do sayHi() --> " + name + " password = " + password;
}
加注解之后,此时的参数就必须传递:
如果参数名不一致,可以在注解的参数中设置重命名,然后设置一个参数是否为必须传递的参数:
//传递多个参数 & 参数重命名
@RequestMapping("/user/sayHi")
public String sayHi(@RequestParam String name,@RequestParam(value = "pwd", required = false) String password) {
return "do sayHi() --> " + name + " password = " + password;
}
@RequestParam 注解中的参数 required 默认值 就是 True,也就是只要后端有这个参数,在前端就必须传递过来;设置为 false 之后, pwd 这个参数就不是 必传的参数了
获取Cookie:
此时需要加注解 @CookieValue,就是获取到 Cookie 中的 value 值,然后在前端设置 Cookie 字段即可(因为已经在注解的参数中指定 Cookie 的key 值了,所以在前端设置的 name 必须和此处是一样的)
//获取一个 cookie 对象
@RequestMapping("/getCookie")
public Object getSession(@CookieValue(name = "java", required = false) String java) {
return java;
}
获取 Header:
通过注解 @getHeader来实现 获取请求头部信息
//获取 Header 中的信息
@RequestMapping("/getHeader")
public String getHeader(@RequestHeader("User-Agent") String userAgent) {
return "userAgent : " + userAgent;
}
在浏览器中找一个请求 Header 查看头部信息,之后将 Key 值复制到 代码中注解的参数中即可;如下图中运行结果:
获取 Session:
首先要想存储一个Session,需要先进行存储,之后才能获取到这个Session,但是存储Session 对象不能使用注解,前文中已经介绍过:Spring MVC 是基于 Servlet API 封装的框架,所以在存储 Session 会话时依然可以使用 servlet API 来存储 Session。如下代码:
private static final String SESSION_KEY = "USERINFO_SESSION_KEY";
//存储一个 Session
@RequestMapping("/setSession")
public void doPostConstruct(HttpServletRequest request) {
//此处需要允许创建一个 Session 会话,因为用户第一次登录是需要创建一个 Session 会话的,
//只有在之后的用户再次的登录的校验中才不允许创建一个会话
HttpSession session = request.getSession();//通过这个请求获取一个 Session
session.setAttribute(SESSION_KEY, "张三");
}
//获取一个Session
@RequestMapping("/getSession")
public Object getSession(@SessionAttribute(SESSION_KEY) String name) {
return "session -- > " + name;
}
由于 Spring MVC框架在进行初始化的时候,初始化的时机是在创建 HttpServletRequest 对象之前的,所以不能将 存储 Session 的业务写在初始化的方法中,因为在 Spring MVC 注入对象时,如果一个方法中有这个参数,就必须在初始化的时候将这个 参数 传递过来,所以我们需要用访问 路由的方式先存储一个 Session,然后再获取 Session 中的Key值,如下图所示:
注:本文中没有介绍热部署,所以在获取参数时,修改代码之后需要重启项目才能看到正确的运行结果。
最后就只剩下数据的返回了,下一篇文章再进行详细介绍。