(一)什么是Spring MVC
1.spring mvc
spring mvc就是基于servlet API构建的原始web框架,从一开始就包含在spring中。
所以我们就可以得出,spring mvc就是一个web框架
2.mvc
mvc是三个英文单词的缩写,model.view.controller,他是一种软件工程架构设计模式,把软件系统分为模型,视图,控制器
视图:指在应用程序中专门用来和浏览器进行交互,展示数据的资源
模型:是程序的主体,用来处理数据逻辑
controller:接收视图发来的清流,并且分发给不同的模型来进行处理
结合mvc,我们又可以得出, spring mvc是一个实现了mvc思想的一个web框架
我们在上一篇博客创建项目中引入的spring web就是spring mvc框架,那这时我们可能会有疑问,上篇博客不是创建的spring boot项目吗,怎么又变成spring mvc了,这里我们要说明spring boot只是实现了spring mvc的一种方式之一除了spring mvc,spring boot还有很多其他的功能
(二)学习spring mvc
我们说spring mvc的本质是web框架,在用户输入url之后,我们spring应该可以根据请求返回响应,所以我们主要分为三个方面:
1.建立连接:将浏览器与java程序进行连接(可以通过url来访问到我们的spring程序)
2.请求:用户发送的url会带有参数,我们要获取这些参数
3.响应:我们接收到用户的参数后,进行处理并返回结果
1.@RequestMapping
这是我们最常用的一个注解,用来注册结构的路由映射,我们url中的文件路径就对应这个
但是我们程序中为什么要再添加@RestController,是因为如果我们不加这个注解,spring不会加载这个类,具体我们会在将RestController说到
注意我们映射可以加"/"也可以不加,如果不加/,spring会给我们自动拼接一个,我们用户也能正常访问也可以正常响应,当然我们除了可以在方法上加路径,也可以在类上加
那我们用户传入请求,这个注解是接收get类型请求还是Post类型的呢?
这时我们需要利用postman来给我们发送一个请求
此时我们可以看到,@RequestMapping是都可以支持的,
但是我们可以通过改变里面的参数,来指定我们接收的方法类型,我们需要改变@RequestMapping中的method参数
我们修改了参数后,只接收get类型的请求,我们通过postman再发送一个post类型的数据就会报405,客户端发送的类型不正确
但是我们注意,在我们添加了method时,ide给我们自动补上了value,这时我们点进他的源码看看
我们可以看到里面有很多参数(这里没截全),如果我们只传入一个参数,那么默认为value值,如果要传入其他参数,那么就需要我们手动加上value=
2.传递和获取参数
1)传递一个参数
我们上面的代码,只是用户访问,返回一个值,那么我们用户想要传值,需要怎么做呢?
这里我们就拿到了传入的值
当然我们也可以通过浏览器传原理和postman是一样的
但是注意,我们这里传的key要严格与我们后端方法的参数名一致,我们后端接收的参数名称是name,那我们就必须要传入name,如果参数名不一致是获取不到参数的
同时如果我们要接收的是一个基本类型的数据,那浏览器就必须给我们传值,不然会报500,如果类型不匹配会报400
我们之前说4xx一般是客户端出错,而5xx一般是后端代码出错了,那出错了spring会给我们报出错误日志
翻译一下就是这个age存在但是不能转为null,我们可以把它定义成对应的包装类型
2)传递多个参数
跟我们传递一个参数一样,不过要注意的是,我们说前后端参数进行匹配时,是按照参数的名称进行匹配的,所以我们可以打乱参数的顺序来传输
3)传递对象
我们可以传递多个参数,那就需要多个形参,这会导致我们的代码比较乱,所以我们可以把它封装成一个对象,用户给我们传一个对象,我们就可以自动拿到对应的值
这里我们一定要写上get和set方法1,这样我们才可以顺利的把值附进去。
看我们如果传入了一个对象,代码是不是就简单明了许多
那我们又要问?那我怎么传对象啊?
我们也可以这么传,但是不是最佳选择
4)JSON对象的转换和接收
我们先来了解下json,简单来说,就是一种数据格式,有自己的格式和语法,用一个文本来表示对象或者数组的信息,本质上就是字符串
json字符串可以很复杂,虽然他只是键值对结构,但是value中可以是另一个对象
json的语法:
数据在键值对key: value中
数据由,分隔,对象由{}表示,数组由[]来表示
对象和json字符串的相互转换
这里我们使用ObjectMapping中提供的两个方法,可以完成相互转换
json的优点:
1.简单易用,表达清晰 2.跨平台可用:json可以被多种语言支持,可跨平台进行数据交换和传输 3.轻量级:相较于xml格式,json更加轻量级,传输数据占用带宽小,可提高数据传输速度 4.利于扩展:json的数据结构灵活,方便扩展和使用
我们要接收一个json对象,需要使用@RequestBody
如果我们去掉了这个注解,会发现值全都为null
5)后端参数重命名
我们上述说客户端传入的名称要严格与我们后端对应,但我们如果不想让用户知道我们后端代码名称是什么,就需要我们使用@RequestParam来修改前后端参数名称。
这样我们就更改了映射的名称
但是我们要注意一旦我们使用了这个注解,就必须严格与@RequestParam名称一直才可以绑定参数和赋值 ,而且使用了这个参数,参数就必须传递
我们来看一下源码
我们可以看到required的默认值为true,默认参数为必传,所以我们可以把require=false来避免不传递的报错
RequestParam和RequestMapping中的method一样,添加了就需要添加value
6)传递数组
后端也可以接收一个数组
此外我们除了可以一次性写完数组中的值,我们也可以分多个参数,也会合理返回
7)传递集合
传递一个集合和一个数组是很类似的
但是为什么我们上述请求错了?
我们传入一个集合需要适合@RequestParam来绑定参数
但是注意我们使用了这个参数就必须要传参数,如果不传需要把require设置成false
8)获取URL中的参数
@PathVariable就是路径变量,我们使用这个注解后就可以通过URL来直接获取参数
如果⽅法参数名称和需要绑定的URL中的变量名称⼀致时,可以简写,不⽤给@PathVariable的属性赋值,如上述例⼦中的id变量
如果⽅法参数名称和需要绑定的URL中的变量名称不⼀致时,需要@PathVariable的属性value赋值, 如上述例⼦中的userName变量
9)上传文件
我们通过MultipartFile 来接收一个文件,同时我们可以通过这个来获取到文件的一些信息比如名字和路径
那我们怎么传入一个文件呢?
我们只需要在body中选择from-data并且把key改成file类型即可
10)获取cookie和session
我们之前在HTTP协议中说到了cookie,当时我们说cookie是一个用户身份标识用来在客户端存用户信息,通常会搭配session来使用,这里我们来说一下这两个,以及他们的区别
session就是一个会话,我们服务器和客户端会不断的发送请求响应,而同一个服务器会给不同的客户端返回不同的响应,但是我们如何去判断响应应该返回给那个用户,请求是从那个用户来的?这时候我们就需要session来保存用户的信息
所以session也是服务器为了保存用户信息而创建的特殊对象
session本质是一个哈希表,他的key叫做sessionid,value是用户信息,通常sessionid是服务器生成的一个特殊字符串,并且我们会把这个sessionid放入到token中,然后返回这个token给客户端,而客户端可以拿到其中的sessionid并且存放到cookie中,这就是我们为什么说session和cookie是会一起使用,而当我们客户端再次访问服务器时,我们就会把携带了sessionid的cookie返回,我们服务器获取到sessionid就可以获取他的用户信息,如果没有就会重新创建sessionid,并且返回
注意,我们的token不仅由sessionid还会有一些其他的信息比如时间和签名等
区别:首先cookie是客户端来保存用户信息的地方,session是服务器保存用户信息的地方
cookie和session是通过sessionid来建立连接的,本身并不相关
cookie保存用户信息也不一定是sessionid,而sessionid也不一定需要cookie传递可以通过url传递
不通过注解获取cookie
如果不使用注解,因为我们spring mvc是使用servlet api构建的web框架,所以我们可以使用它本身提供的api来获取请求,然后获取请求中的cookie
我们可以通过postman或者浏览器来伪造cookie
通过注解来获取cookie
那我们来说一下上面两个有什么不一样
最主要的就是用api获取cookie会一下获取所有的cookie,而使用注解,只会获取到对应的cookie
session的存储和获取
不通过注解获取session
我们要注意session是服务器端的机制,我们需要先进行存储才能获取
上述代码会获取到请求中的session,如果没有会自己设置session
使用注解获取session
11)获取Header
我们传统获取header也是通过HttpServletRequest中获取的,通过调用getHeader就可以获取
在getHeader中写入想要获取的元素
我们也可以通过注解的方式来获取
我们会从请求中获取到User-Agent并且赋值给UA
3.响应
1)返回一个页面
首先我们写一个非常简单的页面
然后我们尝试返回这个页面的路径
我们会发现并没有返回这个界面,只是返回了这个字符串,那我们如何把这个作为一个页面路径返回呢?我们需要把最上面的注解@ResrController改为@Controller
这会就会把我们这个当作页面路径返回
那为什么改为这个注解就会变成页面路径返回呢?
我们之前看@RestController的源码,我们发现@RestController =@Controller +@ResponseBody
我们如果使用@Controller就会把返回值当作页面路径返回,@ResponseBody就是把返回值当成数据返回,@RestController和@ResponseBody一样都是返回数据
我们注意ResponseBody是类注解也是方法注解,如果加在类上,就代表类内的方法返回的都是数据,如果类内有返回数据也有返回页面的就可以在对应的方法上加上对应的注解
如果我们要返回界面却没有使用Controller就会返回404因为会按照返回的数据去寻找页面,找不到就会返回404
2)返回HTML代码片段
返回i数据时如果有HTML代码就会被浏览器解析
3)返回json
4)设置状态码
所以我们可以手动的返回状态码
5)设置Header
-
value:指定映射的URL
-
method:指定请求的method类型,如GET,POST等
-
consumes:指定处理请求(request)的提交内容类型(Content-Type),例如application/json, text/html;
-
produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
-
Params:指定request中必须包含某些参数值时,才让该⽅法处理
-
headers:指定request中必须包含某些指定的header值,才能让该⽅法处理请求
那如果我们想设置其他的Header的话就需要使用HttpServletResponse提供的方法来设置
通过内置的response.setHeader来设置
void setHeader(Stringname,Stringvalue)设置⼀个带有给定的名称和值的header.如果name 已经存在,则覆盖旧的值.
4.一个简单的综合练习
我们来简单做一个加法器
首先,我们在工作时由于涉及到前后端交互人员的变动,所以我们一般会约定一个接口文档,也就是约定一些必要的参数,名称和返回值等,包括参数名称,映射名称等等
那我们要设计一个加法器,那么来定义一个简单的接口
请求路径:"/text/calc"
请求方式:get/post
接口描述:计算两个整数相加
接收参数:num1,num2(类型Integer)
我们用from表单来提交请求