Spring MVC(1)---介绍Spring MVC 和 请求数据

(一).介绍Spring MVC

1.具体概念

Spring MVC,通常我们都是叫做Spring Web MVC,是基于Servlet API 构建的原始框架。总结来说,Spring Web MVC 是一个Web框架, 简称为Spring MVC。

MVC,即 Model View Controller,是软件工程中的一种软件架构设计模式,它把软件系统分为模型,视图和控制器三部分

view:指在应用程序中专门用来与浏览器进行交互,展示数据的资源

model:是应用程序的主体部分,用来处理程序中数据逻辑的部分

controller:可以理解为分发器,用于决定对于视图发来的请求,需要从哪一个模型来处理,以及处理完后需要跳回到哪一个视图,即用来连接视图和模型

MVC是一种架构设计模式,也是一种思想,而Spring MVC是对MVC思想的具体实现。除此之外,Spring MVC还是一个Web框架。总的来说,Spring MVC是一个实现了MVC模式的Web框架。

事实上,在前面介绍Spring快速入门的时候,已经使用过Spring MVC了,在创建Spring Boot项目的时候,我们选中了Spring Web 框架,其实就是Spring MVC。


Spring Boot 只是实现Spring MVC的其中⼀种⽅式⽽已.
Spring Boot 可以添加很多依赖, 借助这些依赖实现不同的功能. Spring Boot 通过添加Spring Web
MVC框架, 来实现web功能。不过Spring在实现MVC时, 也结合⾃⾝项⽬的特点, 做了⼀些改变, 相对⽽⾔, 下⾯这个图或许更加合适⼀些.

只不过现在基本没有View了,现在后端都是直接返回数据到浏览器上,不通过view了。

2.学习SpringMVC

在学习Spring MVC的时候,主要是学习如何通过浏览器和用户程序进行交互。主要分为三个方面①.建立连接②.请求③.响应

(二).建立连接

1.基础概念

在Spring MVC中,使用@RequestMapping注解来实现URL路由映射,也就是浏览器连接程序的作用。下面通过一个例子来看。

可以看到,在浏览器中通过访问URL,得到了后端的数据。可能有人会问了,既然@RequestMapping可以完成任务了,那么为什么还要加个@RestController ?

当我将@RestController去掉之后,重新运行,发现报错了,状态码为404,这个状态码在JavaEE初阶的时候介绍过。这个因为一个项目中会有很多类,每个类可能由很多个方法

随便点一个就很多。Spring在启动的时候,会对所有的类进行扫描,如果类加了@RestController或@Controller(@RestController=@Controller + @ResponseBody),Spring才会去看这个类里面的方法有没有加@RequestMapping这个注解,当然关于@RestController这个注解的作用还不止这一点,后面还会详细介绍。

这是@RestController的原码

2.@RequestMapping的使用

@RequestMapping既可以修饰类,也可以修饰方法。修饰类的时候称为"类注解",修饰方法的时候,称为"方法注解"。

3.@RequestMapping的请求方式

(1).既支持GET,也支持POST

@RequestMapping既支持GET请求,也支持POST请求

通过上面的抓包,可以看出是一个GET请求

如果想要是POST请求,则需要通过form表单来构造请求

也可以在后面跟数组,这样也可以

java 复制代码
    @RequestMapping(value = "/func1",method = {RequestMethod.GET,RequestMethod.POST})
    public String func1(){
        return "func1";
    }

(2).只支持GET

方式1:
java 复制代码
    @RequestMapping(value = "/func2",method = RequestMethod.GET)
    public String func2(){
        return "func2";
    }

可以看到,我在@RequestMapping中添加了一个method属性,然后指定方法为GET

这是method的原码,主要用途就是指定请求方式的,可以看到,是一个枚举数组,如果只跟一个值的话,不需要加大括号,如果多个值的话就需要加大括号

方式2:
java 复制代码
    @GetMapping("/func3")
    public String func3(){
        return "func3";
    }

直接通过@GetMapping注解,来指定请求方式为GET

(3).只支持POST

方式1:
java 复制代码
    @RequestMapping(value = "/func4",method = RequestMethod.POST)
    public String func4(){
        return "func4";
    }

在@RequestMapping中添加了一个method属性,然后指定方法为POST

方式2:
java 复制代码
    @PostMapping("/func5")
    public String func5(){
        return "func5";
    }

直接通过@PostMapping注解,来指定请求方式为POST

4.下载Postman

(1).具体介绍

Postman,可以理解为是一个测试工具,当我们写完代码后,都是需要进行测试的,这时候就可以使用Postman来进行测试。使用Postman进行测试和使用浏览器进行测试,本质上是一样的效果

(2).使用规则

(3).传参介绍

①.普通传参,即通过查询字符串来传参
②.form-data传参

表单提交数据,通常用于提交图片或文件

选择Body -> form-data 选项

③.x-www-form-urlencoded

form表单

④.raw

可以上传任意格式的文本,可以上传text,json,xml,html等等

(三).请求数据

对于请求数据,当访问不同的路径的时候,就是发送的不同的请求,在发送请求的时候,可能会带一些参数,下面就来介绍如何携带参数

1.传递单个参数

java 复制代码
    @RequestMapping("/r1")
    public String r1(String username){
        return "接收参数:" + username;
    }

注意:在postman中测试的时候,传递参数的名称要和后端一样,否则会出错

2.传递两个参数

java 复制代码
    @RequestMapping("/r2")
    public String r2(String username,String password){
        return "接收参数:用户名"+username+" 密码"+password;
    }

3.基本类型接收和包装类型接收的区别

java 复制代码
    @RequestMapping("/r3")
    public String r3(Integer num){   //包装类型
        return "接收参数:"+num;
    }

    @RequestMapping("/r4")
    public String r4(int num){     //基本类型
        return "接收参数:"+num;
    }

可以看到,当传递值的时候没有区别

当没传递值的时候,区别就出来了,没传递值的时候,基本类型直接报错,而包装类型则为null

4.传递对象

java 复制代码
package org.review.blog.springbootblog.model;

public class TestUserInfo {
    private String name;
    private int age;
    private int gender;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getGender() {
        return gender;
    }

    public void setGender(int gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "TestUserInfo{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                '}';
    }
}

上面是我定义了一个User类,然后我们就开传递这个User类创建的对象

java 复制代码
    @RequestMapping("/r5")
    public String r5(TestUserInfo userInfo){
        return "接收参数:"+userInfo.toString();
    }

上图这两种方式都可以,一种是通过get传输的,一种是通过post传输的

主要是把这个对象当成"请求参数对象"进行处理

5.绑定参数,重命名参数

先通过一个示例来看

当我搜索一个"qqq"的时候,在url的查询字符串中显示了"q=qqq",这个可能是前端进行了一些加密手段,但是对于后端来说,并不知道这个"q"是什么意思,所以当前端向后端发请求的时候,由于后端不知道"q"的具体含义是什么,所以就无法进行处理。此时,解决方法就是,后端对参数进行重命名。

java 复制代码
    @RequestMapping("/r6")
    //将前端传过来的q 和 keyword进行绑定,如果不重命名,前端直接传keyword则会直接报错
    //参数绑定的意思主要是,后端不理解q是什么意思,所以通过直到该参数的意思的变量来接收这个参数
    public String r6(@RequestParam("q") String keyword){
        return "接收参数:"+keyword;
    }

同时,使用了@RequestParam这个注解后,表示这个参数是一个"必传参数",如果不传,则直接报错

如果想要避免这种情况,可以在后面加上一个"require"属性,并设置为false

java 复制代码
    @RequestMapping("/r6")
    //将前端传过来的q 和 keyword进行绑定,如果不重命名,前端直接传keyword则会直接报错
    //参数绑定的意思主要是,后端不理解q是什么意思,所以通过直到该参数的意思的变量来接收这个参数
    public String r6(@RequestParam(value = "q",required = false) String keyword){
        return "接收参数:"+keyword;
    }

这样,即使不传参数,也不会报错

事实上,@RequestParam说是参数重命名,事实上就是"参数绑定",让"q"这个参数和"keyword"这个参数进行绑定
注意:@RequestParam是用来接收URL查询参数或表单数据的,无法直接解析请求体里的JSON数据,所以不能使用JSON传输

6.传递数组

java 复制代码
    @RequestMapping("/r7")
    public String r7(String[] arr){
        return "传递参数:"+Arrays.toString(arr);
    }

这两种方式都可以

7.传递集合

java 复制代码
    @RequestMapping("/r8")
    public String r8(@RequestParam List<String> list){
        return "接收参数:"+list;
    }

注意:这里使用@RqeustParam的原因是,默认情况下接收的是一个数组,然后让这个数组和list进行绑定

注意:不能使用JSON传输,原因和上面的一样

8.传递JSON

(1).介绍JSON

java 复制代码
{
 "squadName": "Super hero squad",
 "homeTown": "Metro City",
 "formed": 2016,
 "secretBase": "Super tower",
 "active": true,
 "members": [{
 "name": "Molecule Man",
 "age": 29,
 "secretIdentity": "Dan Jukes",
 "powers": ["Radiation resistance", "Turning tiny", "Radiation 
blast"]
 }, {
 "name": "Madame Uppercut",
 "age": 39,
 "secretIdentity": "Jane Wilson",
 "powers": ["Million tonne punch", "Damage resistance", "Superhuman 
reflexes"]
 }, {
 "name": "Eternal Flame",
 "age": 1000000,
 "secretIdentity": "Unknown",
 "powers": ["Immortality", "Heat Immunity", "Inferno", 
"Teleportation", "Interdimensional travel"]
 }]
}

上面的代码就是一段JSON代码。JSON语法分为这几点:①.数据在 键值对(Key/Value) 中 ②.数据用 " , " 隔开 ③.对象用 " { } "表示④.数组用 " " 表示,值可以为对象,也可以为数组,数组中可以包含多个对象

常见的JSON和对象的转换工具有三个:①.gson,是由谷歌推出的 ②.fasjson 是由阿里巴巴推出的 ③.jackon 是Spring内部就集成了的

如果没有引依赖,则需要引入依赖;如果已经引入依赖了,则通过ObjectMapper这个类中的readValue()方法,将JSON转成对象,通过ObjectMapper这个类中的writeValueAsString()方法,将对象转为JSON

由于下面主要是使用这两个方法,所以我们直接在test这个包中写测试代码

java 复制代码
    //对象转JSON
    @Test
    void ObjecttoJson(){
        TestUserInfo testUserInfo=new TestUserInfo();
        testUserInfo.setName("zhangsan");
        testUserInfo.setAge(21);
        testUserInfo.setGender(1);
        ObjectMapper objectMapper=new ObjectMapper();
        String str = objectMapper.writeValueAsString(testUserInfo); //进行转换
        System.out.println(str);
    }

这里使用@Test注解,主要目的就是为了标记,"标记"这是一个单元测试方法,这个单元测试方法是一个可以独立运行的测试方法

java 复制代码
    //JSON转对象
    @Test
    void JsontoObject(){
        ObjectMapper objectMapper=new ObjectMapper();
        String str="{\"age\":21,\"gender\":1,\"name\":\"zhangsan\"}";
        TestUserInfo testUserInfo = objectMapper.readValue(str, TestUserInfo.class);  //进行转换
        System.out.println(testUserInfo);
        System.out.println(testUserInfo.getName());
    }

(2).传输JSON

这里的传递JSON,指的是,前端向后端传递JSON格式的数据

使用@RequestBody注解的原因是,把请求的正文JSON转换成TestUserInfo类型。因为前端只能传输JSON,不能传输对象,所以前端传的时候是通过JSON传的,然后通过@RequestBody调用Jackson的消息转换器用ObjectMapper把JSON字符串反序列化成Java对象。

测试的时候必须传json数据 按 请求体 JSON 绑定 必须用 POST/PUT 等带 Body 的方法

9.从URL中获取参数

这是我从"今日头条"上,找到了一个案例。如果说,想要获取到URL中的参数的话,该如何操作?

java 复制代码
    @RequestMapping("/r10/{number}")
    public String r10(@PathVariable Integer number){
        return "接收参数:"+number;
    }

要获取URL中的参数,首先要使用**@PathVariable** 注解,表示为**"路径的变量"**注解,同时要在方法路径中,将要被获取的URL参数使用 " { } " 括起来

同时,也可以获取多个参数

java 复制代码
    @RequestMapping("/r11/{type}/{number}")
                      //也可以支持重命名,重命名了之后表示"该参数必传"
    public String r11(@PathVariable("type") String type111,@PathVariable Integer number){
        return "接收参数:"+type111+number;
    }

10.上传文件

java 复制代码
    @RequestMapping("/r12")
    public String r12(MultipartFile file) throws IOException {
        System.out.println(file.getOriginalFilename());
        file.transferTo(new File("E:\\test\\"+file.getOriginalFilename()));  //上传文件
        return "上传成功";
    }

同时也是支持重命名的

11.获取Cookie,存储Session,获取Session

(1).回顾Cookie和Session

Cookie最好的例子就是去医院挂号。在去医院挂号的时候,首先护士会询问你一些基本信息,例如,姓名,身份证号,性别,年龄,病状等信息,然后就会给你一张"就诊卡","就诊卡"始终在你自己的手里。当你去看医生的时候,医生也是通过刷你的"就诊卡"就知道了你的一些基本信息,每次看病的时候,医生都会刷"就诊卡"。此时,这个就诊卡就相当于"Cookie"。当医生刷你的"就诊卡"的时候,刷的一瞬间,就会向医院的服务器里,去找关于你的数据,此时医院的服务器就是"Session"。

关于Cookie和Session,出现的原因是因为,HTTP协议是**"无状态的"** 。即客户端和服务器这次的通信,和下次通信之间是没有直接的联系的。例如,当我第一次访问CSDN的时候,我首先要进行登录,当登录完成后,我就可以看我要找的博客了。看完之后,我就退出了。当下次再访问CSDN的时候,他还要我重新登录,此时就是"无状态的"。但是,每次访问都需要重新登陆,就很麻烦,所以我就希望CSDN能够记住我,当我下次再重新访问CSDN的时候,不需要再重新登陆了,此时就出现了**"Cookie"**。

就类似于上图的效果。上述图中的 "令牌" 通常就存储在 Cookie 字段中.

注意:Cookie是存储在客户端中的,Session是存储在服务器中的。就像"就诊卡",就诊卡一直在你的手中。

Session,即"会话"。

站在服务器的角度,服务器同一时刻收到的请求是很多的,例如,同一时间,我访问了CSDN的服务器,同时,张三,李四,王五,都访问了CSDN服务器。那么服务器就需要区分清楚每个请求属于哪个用户,此时就出现了"Session"。Session是服务器为了保存用户信息而创建的一个特殊的对象。

那么站在服务器的角度,服务器是如何区分不同的Session对象的?

答:通过SessionId。当客户端第一次请求服务器的时候,服务器中并没有保存客户端的信息,此时服务器就创建了一个Session,然后SessionId也就随之生成了,然后服务器在"响应"的时候,通过Set-Cookie的方式,存储到了客户端的Cookie中。当下次客户端再次访问服务器的时候,直接通过SessionId来进行访问

事实上,Session的本本质就是一个"哈希表"没存储了一些键值对结构,Key就是SessionId,Value就是用户的相关信息

当不同的用户访问服务器的时候,就会通过sessionId进行区分

重点:

①.Cookie是客户端保存用户信息的一种机制;Session是服务器端保存用户信息的一种机制

②.Cookie和Session之间主要是通过SessionId关联起来的,SessionId是Cookie和Session之间的桥梁

③.Cookie和Session经常会在一起配合使用,但不是必须配合。可以用Cookie来保存一些数据在客户端,这些数据不一定是用户身份信息,也不一定是SessionId;Session中的sessionId也不需要非得通过Cookie/Set-Cookie传递,比如通过URL传递

(2).具体代码

Ⅰ.获取Cookie

上图是postman设置Cookie的方法

方式①:老版本,不适用注解
java 复制代码
    @RequestMapping("/r13")
    public String r13(HttpServletRequest request){
        Cookie[] cookies = request.getCookies();
        if (cookies!=null){
            for(Cookie cookie:cookies){
                System.out.println(cookie.getName()+" : "+cookie.getValue());
            }
            return "获取成功";
        }
        return "获取失败";
    }

这里我们使用了一个新的类,HttpServletRequest,对应的还有一个类HttpServletResponse。在前面JavaEE初阶的时候,我们学习了Http协议,包括报头等信息,HttpServletRequest中的方法,可以设置HTTP协议中报头的所有数据

方式②:新版本,使用注解
java 复制代码
    @RequestMapping("/r14")
    public String r14(@CookieValue("name") String name){
        return name;
    }

直接通过注解@CookieValue来获取cookie值

Ⅱ.存储Session

存储Session依旧使用HttpServletRequest类中的方法

java 复制代码
    @RequestMapping("/r15")
    public String r15(HttpServletRequest request){
        //先从前端发来的请求中找到Cookie,然后从Cookie中找到SessionId,然后通过SessionId找到Session对象
        HttpSession session = request.getSession();//如果sessionid不存在,则创建对象;如果存在,那么setAttribute会替代当前值
        session.setAttribute("name","zhangsan");
        session.setAttribute("age",1);
        return "session设置成功";
    }

这个方法目前不太好测,需要结合"获取Session"来进行测试

Ⅲ.获取Session
方式①:
java 复制代码
    @RequestMapping("/r16")
    public String r16(HttpServletRequest request){
        //先从前端的请求中找到Cookie,然后从Cookie中找到SessionId,然后通过SessionId找到Session对象
        HttpSession session = request.getSession(false);  //这里传false的原因:如果session不存在,则直接返回null,不会再进行创建新的session
        if (session!=null){
            //从Session中获取用户登录信息
            String name = (String) session.getAttribute("name");
            return "姓名:"+name;
        }else{
            return "用户未登录";
        }
    }

当我还没存储Session的时候,先获取Session

显示的是"用户未登录"

当我存储Session之后,再进行获取Session

可以看到,可以正常获取了

重点:

此时我通过postman来获取session的时候,cookie尾号为"40CE"

我通过浏览器来获取session的时候,cookie尾号为"378E"

所以,通过不同的客户端进行访问,获取到的值是不一样的。

当我将浏览器的值改了之后

发现,显示的"用户未登录"。这就是因为,当调用"获取Session"这个接口的时候,会从cookie中找SessionId,由于并没有这个SessionId创建的session,所以就显示"用户未登录"了

方式②:
java 复制代码
    @RequestMapping("/r17")
    public String r17(HttpSession session) {
        if (session == null) {
            return "用户未登录";
        }
        String name = (String) session.getAttribute("name");   // 简化了 HttpSession session = request.getSession();
        return "姓名:"+name;
    }
方式③:
java 复制代码
    @RequestMapping("/r18")
    public String r18(@SessionAttribute("name") String name){
        return "姓名:"+name;
    }

直接使用@SessionAttribute注解

12.获取Header中的其他属性

就拿获取"User-Agent"来说吧

方式1:

java 复制代码
    @RequestMapping("/r19")
    public String r19(HttpServletRequest request){
        String UA = request.getHeader("User-Agent");
        return "User-Agent 的内容为 :"+UA;
    }

方式2:

java 复制代码
    @RequestMapping("/r20")
    public String r20(@RequestHeader("User-Agent") String UA){
        return "User-Agent 的内容为 :"+UA;
    }

直接使用@RequestHeader注解

相关推荐
DianSan_ERP1 小时前
架构师视角:电商大促高并发下的订单API限流与防漏单架构演进
java·运维·网络·安全·微服务·架构·自动化
云烟成雨TD1 小时前
Agent Scope Java 2.x 系列【6】消息层
java·人工智能·agent
云烟成雨TD1 小时前
Spring AI Alibaba 1.x 系列【74】Agentic RAG 与混合 RAG
java·人工智能·spring
小刘|1 小时前
Spring AI 结构化输出 + 大模型参数全解(含千问调优)
java·后端·spring
云烟成雨TD1 小时前
Spring AI Alibaba 1.x 系列【79】图执行生命周期的可观测性基础设施
java·人工智能·spring
霸道流氓气质1 小时前
Java 单元测试生成大量 Excel 测试数据实战指南
java·单元测试·excel
io无心1 小时前
基于Image 2的多配件商品图生成技术实现(已开源)
java·image2
逢君学术论文AI写作1 小时前
Java第22课:Servlet获取请求参数+POST请求+表单交互
java·servlet·ai写作
神明不懂浪漫1 小时前
【第二章】Java中的数据类型,运算符与程序逻辑控制
java·开发语言·经验分享·笔记