初始JavaEE篇 —— Spring Web MVC入门(上)

news2025/2/9 0:47:49

 找往期文章包括但不限于本期文章中不懂的知识点:

个人主页:我要学编程程(ಥ_ಥ)-CSDN博客

所属专栏:JavaEE

目录

@RequestMappingg 注解介绍

Postman的介绍与使用

PostMapping 与 GetMapping 注解 

构造并接收请求

接收简单参数

接收对象 

接收数组 

接收集合 

接收JSON数据 

JSON的介绍 

JSON字符串与Java对象互转

从URL中获取路径参数(接收路径参数)

接收文件(上传文件)

获取Cookie/Session

获取Cookie

Session存储与获取

获取Header 


知道如何去创建SpringBoot项目之后,我们就可以开始学习Spring Web MVC,其主要是学习如何使用 Spring 框架来构建 Web 应用程序,尤其是遵循 MVC(Model-View-Controller)设计模式。但是现在开发都是前后端分离的,因此MVC设计模式也算是有点老了,因此我们的侧重学习点是Web。Spring Web MVC 是 Spring Framework 的一部分,而SpringBoot是对Spring Framework的一个封装,因此后续学习Spring Web MVC 都是创建的SpringBoot项目,其包含了Spring Web MVC。Web的学习主要是分为两大核心:请求 与 响应。

前面在快速上手SpringBoot中,我们访问HelloWorld时,需要先在浏览器的地址栏中输入URL,这就是一个请求,虽然在客户端很简单,但是在服务端的编写是需要再普通类和方法上加上注解,我们就先来学习一下注解。

@RequestMappingg 注解介绍

RequestMapping 翻译过来的意思是 请求映射,但更常见的称呼是路径映射,即通过浏览器来访问时,其对应的资源路径是啥。如下所示:

路径映射既可以映射相应的类,也可以映射到相应的方法。上面只是映射到相应的方法,下面来演示一下映射到相应的类:

@RestController
@RequestMapping("/HelloController") // 映射类
public class HelloController {
    @RequestMapping("/hello") // 映射方法
    public String hello() {
        return "Hello World";
    }
}

注意:

1、如果我们此时还是去访问hello的话,就会出现下面这样的错误:

右下方有提示:Not Found 以及 404 都是在告诉我们资源不存在,也就是客户端错误。 

2、我们最终访问的都是具体的方法,而不是类,因此我们在输入URL时,都得输入对应的方法所在的路径映射(如果所在的类也有的话,也得加上)。 

3、在日常开发中,我们都会为类以及方法都加上对应的路径映射。

4、最终的资源路径一定要是唯一的才行,不能出现一个资源路径对应着多份资源,这是错误的。但有例外的情况:

@RestController
@RequestMapping("/HelloController") // 映射类
public class HelloController {
    @RequestMapping("/hello") // 映射方法
    public String hello() {
        return "Hello World";
    }
}


@RequestMapping("/HelloController") // 映射类
class HelloController2 {
    @RequestMapping("/hello") // 映射方法
    public String hello() {
        return "Hello World";
    }
}

虽然同一个资源路径对应着多份资源,但是这里 HelloController2 并不会在公开出去。带有@RestController注解的类会被 Spring 扫描并注册为控制器,简单理解就是带有该注解的类会被Spring看成正规军,正规军中的所有人必须配备一套装备,而备用军可以存在多个人公用一套装备的情况。这里的路径映射唯一是指在带有@RestController注解的类中。还有一种特殊情况:请求的方式不同,资源路径相同时没关系的。例如,一个是 GET请求,一个是 POST请求。

5、这里的路径映射可以是多层路径,且最前面的斜杠可以去掉,但是后面的不行。如下所示:

@RequestMapping("UserController")
@RestController
public class UserController {
    @RequestMapping("User/Admin")
    public String user() {
        return "我要学编程,你好!";
    }
}

虽然路径映射在第一层不需要我们自己加上斜杠,但是在为了规范我们还是需要加上。虽然路径映射中可以带有中文和空白字符,但同样开发规范是不允许的。

Postman的介绍与使用

当前最为主流的开发模式:前后端分离。在这种模式下,前端技术人员基于"接口文档",开发前端程序;后端技术人员也基于"接口文档",开发后端程序。由于前后端分离,对我们后端技术人员来讲,在开发过程中,是没有前端页面的,那我们怎么测试自己所开发的程序呢?

方式1:像前面那样,直接使用浏览器。在浏览器中输入地址,测试后端程序

 弊端:在浏览器地址栏中输入地址这种方式都是GET请求,如何我们要用到POST请求怎么办呢?要解决POST请求,需要程序员自己编写前端代码(比较麻烦)

方式2:使用专业的接口测试工具(Postman工具)

下载链接:Download Postman | Get Started for Free

下载好了之后,我们就可以来简单使用了:

1、创建仓库:

  

通过上面的方式就是已经创建了一个新的空白仓库了。 

2、模拟请求并发送:

  

3、接收响应:

  

如果我们想要保存上述的请求话,可以直接点击右上方的save保存到某个仓库中,或者在创建新仓库时直接创建新的请求: 

  

总之创建请求的方式有很多,随意选择一种即可。 

PostMapping 与 GetMapping 注解 

上述发送请求与接收响应都是最基础的。我们也可以来测试RequestMapping支持啥样的请求。 

由此可见:RequestMapping既支持GET请求,也是支持POST请求的。那可以限制吗?可以的。

    // 限制了s1只支持POST请求
    @RequestMapping(value = "/s1",method = RequestMethod.POST)
    public String s1() {
        return "hello s1";
    }

    // 限制了s2只支持GET请求
    @RequestMapping(value = "/s2",method = RequestMethod.GET)
    public String s2() {
        return "hello s2";
    }

先构造出上面两个方法,我们再使用Postman来构造请求并发送,观察其响应。

同理,我们对s2构造POST请求时,也会出现上述的情况。 

限制方法的请求除了上述使用method参数来限制之外,还可以使用 GetMapping 注解  PostMapping 注解:

    // 只支持GET请求
    @GetMapping("/s1")
    public String s1() {
        return "hello s1";
    }

    // 只支持POST请求
    @PostMapping("/s2")
    public String s2() {
        return "hello s2";
    }

这里就不再演示效果了。

注意:请求方法的分类,我们在前面学习HTTP请求时,已经学习完了,虽然有好几种,但在日常的开发中,我们只会使用 GET 和 POST。因此这里演示也只是使用这两种。

构造并接收请求

我们在访问的各种资源时,就是在发送不同的请求,有些请求是需要参数的,因此我们接下来就是学习如何接收不同的参数。

接收简单参数

我们先来看接收一个参数的情况:

@RestController
@RequestMapping("/Request")
public class RequestController {
    @RequestMapping("/r1")
    public String r1(String query_string) {
        return "接收的参数为:"+query_string;
    }
}

上述图中虽然传输了一个参数,但是这个参数并未被我们的代码给接收到,这是因为我们在构造是的参数和代码参数列表中的参数名称并不一致,只有当我们的参数名称一致时,传递的参数也是有效参数。

上面是一种参数的情况,可能会出现多个参数。

    @RequestMapping("/r2")
    public String r2(String userName, String password) {
        return "用户名:"+userName+" 密码:"+password;
    }

上述这种方式虽然可以传递多个参数,但是在有些场合下,会被认为是不安全的传输。如果在传输用户账户的过程中,采用这种方式可能被分专业人员认为这是不安全的,因为我们把密码已经暴露出去了。但是我们学习了HTTPS之后,就会明白安不安全是看在信息被别人截取的情况下,别人是否能够知道里面的信息,即是否为加密传输。

上述在地址栏中的传输信息就是典型的GET请求,而在正文中的传输信息就是典型的POST请求

注意:

1、客户端在传输参数时,要符合Java的语法规范,否则就会发生客户端错误。例如,需要的参数是一个 Integer 类型,而客户端在传输时,传递的是一个 String 类型,这就会发生客户端错误。 

2、如果我们的代码在接收参数时,是基本数据类型的话,在面对数据类型传输不符合语法与传输正常时,都是一样的结果。但是在面对不传输参数时,基本数据类型对应的代码就会报错。因为当我们不传参数时,默认是赋值null,而null对应的是对象,在基本类型看来,需要进行解包操作,而对null进行解包就会报错。如下所示:

    @RequestMapping("/r3")
    public String  r3(int number) {
        return "接收的参数为:"+number;
    }

    @RequestMapping("/r4")
    public String r4(Integer number) {
        return "接收的参数为:"+number;
    }

但是也有一种例外,对于boolean类型的数据来说, 即使是基本数据类型,不传参数,也是会有默认值false的,而不是null,这是Spring框架本身做的特殊处理。

接收对象 

参数到后面可能越来越多,不可能直接传输参数,我们就需要封装成对象。例如,在登录某个网站时,用户在前端界面上输入了相关信息(可能有非常多的信息),这时传输到后端时,不会用一个一个的参数来接收,而是通过封装成对象来接收(前端先封装成对象了)。

创建一个UserInfo类:

public class UserInfo {
    // 属性
    private String name;
    private String gender;
    private Integer age;

    // getter 与 setter 方法
    public String getName() {
        return name;
    }

    public String getGender() {
        return gender;
    }

    public Integer getAge() {
        return age;
    }

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

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

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

    // 重写toString方法
    @Override
    public String toString() {
        return "UserInfo{" +
                "name='" + name + '\'' +
                ", gender='" + gender + '\'' +
                ", age=" + age +
                '}';
    }
}
    @RequestMapping("/r5")
    public String r5(UserInfo userInfo) {
        return "接收的参数为:"+userInfo;
    }

在Postman的界面就只需要将构造参数即可,Postman会自动将这些参数构造成一个对象。这就和用户只需要输入对应的参数即可,前端的逻辑会将这些参数构造成一个对象。

注意:这些参数的名称要和后端代码的属性的名称一致。 

既然每次在传输时,参数要前后端保持一致的话,那有没有什么办法将参数重命名呢?前端只需要输入简单的首字母,传输到后端时,再对传输的参数重命名即可,这样就简化了前端传输的繁琐。

    @RequestMapping("/r6")
    public String r6(String query_string) {
        return "接收的参数为:"+query_string;
    }

    // 通过 @RequestParam("q")注解 将 q 赋值给 query_string
    @RequestMapping("/r7")
    public String r7(@RequestParam("q") String query_string) {
        return "接收的参数为:"+query_string;
    }

对于 r6 来说,前端的参数名称只能是 query_string,但是对于 r7 来说,前端的参数可以是 q,后端在接收时,会将 q 赋值给 query_string,这就简化了前端,也同时提高了后端代码的可读性。

但同时也有一个弊端:对于 r6 来说,当前端传输的参数不是 query_string 时,最终返回的值是 null,而对于 r7 来说,如果前端参数名称与 @RequestParam("q") 中的 q不同时,就会报错:客户端错误,也就是说加上这个注解之后,参数就是必传参数了。

如果想要修改为非必传参数也是可以的。

@RequestMapping("/r7")
public String r7(@RequestParam(value = "q",required = false) String query_string) {
    return "接收的参数为:"+query_string;
}

接收数组 

    @RequestMapping("/r8")
    public String r8(String[] str) {
        return "接收的参数为:"+ Arrays.toString(str);
    }

传输数组参数时,有两种方式:

1、使用多个相同参数的名称传递:

2、 一个参数中传输多个值:

接收集合 

    @RequestMapping("/r9") 
    public String r9(List<String> list) {
        return "接收的参数为:"+list;
    }

如果我们也是按照传递数组的方式去传递集合的话,服务端就会报错。

 IDEA中的错误日志如下:

使用传输数组的方式传输集合时,默认就是数组,而不是集合,我们得使用参数重命名的方式来修改:

    @RequestMapping("/r9")
    public String r9(@RequestParam List<String> list) { // 将数组映射到List
        return "接收的参数为:"+list;
    }

接收JSON数据 

JSON的介绍 

首先得知道什么是JSON?简单来说,JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,用于数据的序列化和传输。它易于阅读和编写,同时也易于机器解析和生成,广泛应用于网络应用程序之间进行数据交互。

JSON的语法非常简单,主要有两种结构组成:数组和对象。

对象由大括号 {} 包围,内部是键值对组成,键与值之间采用 冒号 分隔,键值对之间采用 逗号 分隔。键必须是字符串,字符串需要用双引号引起来。  

{
  "key1": value1,
  "key2": value2
}

数组由方括号 [] 包围,内部包含多个值,值之间用逗号分隔。

数组中的元素可以是任何类型:字符串、数字、布尔值、数组、对象或 null。

[
  value1,
  value2,
  value3
]

下面就是一个JSON数据:

{
  "name": "我要学编程",
  "age": 20,
   "gender": "男"
}

我们在书写JSON数据时,可以使用 在线的JSON格式化工具 来校验和书写:

这种工具直接在浏览器中搜索即可:JSON中文网等。

JSON字符串与Java对象互转

JSON本质上是一个字符串,通过文本来存储和描述数据。我们也可以通过一些依赖将其转为Java中的对象,也可以将Java中的对象转换为JSON数据。由于SpringBoot项目中已经集成了JSON转换工具,因此我们可以直接使用,而不需要导入依赖。

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.54</version>
</dependency>

上述只是JSON数据与Java对象互转的一个依赖,除此之外还有 Jackson、Gson、FastJson。而SpringBoot项目中集成的就是Jackson。

下面就来演示一下JSON与Java对象的互转:

public class JSONUtils {
    public static void main(String[] args) throws JsonProcessingException {
        UserInfo userInfo = new UserInfo();
        userInfo.setName("我要学编程");
        userInfo.setAge(20);
        userInfo.setGender("男");

        ObjectMapper objectMapper = new ObjectMapper();
        // Java对象转换为JSON数据:writeValueAsString(Java对象)方法
        String json = objectMapper.writeValueAsString(userInfo);
        System.out.println(json);
        // 将JSON数据转换为Java对象:readValue(JSON数据,该类的 Class 对象)方法
        UserInfo u1 = objectMapper.readValue(json, UserInfo.class);
        System.out.println(u1);
    }
}

结果:

Jackson是使用ObjectMapper对象提供的两个方法,可以完成对象和JSON字符串的互转。writeValueAsString:把对象转为JSON字符串,readValue:把字符串转为对象。

注意:不同依赖对于JSON与Java对象互转的方法不一样,上面演示的是SpringBoot项目中集成的依赖:Jackson。

上述都只是在学习JSON数据,以及在Java中怎么实现JSON与Java对象的互转,下面我们来学习如何传递JSON数据。

    @RequestMapping("/r10")
    public String r10(@RequestBody UserInfo userInfo) {
        return userInfo.toString();
    }

后端在接收JSON数据时,并不是真的接收JSON数据,而是接收Spring已经给我们转换好的Java中的对象,我们就得使用注解 @RequestBody 标识需要转换为哪个对象才行。

注意:虽然Spring将转换的细节都封装好了,但我们还是需要了解的。

1、ObjectMapper对象在调用readValue方法,将JSON数据转换为对象时,该对象所对应的类一定要有无参的构造方法和getter方法。因为在转换为对象时,是先通过无参的构造方法创建出一个对象,在通过setter方法给对象的属性赋值。因此如果该类没有无参的构造方法,最终就会报错。但是由于反射机制的存在,即使我们不提供setter方法,通过getter方法也是可以转换成功的。因此需要无参的构造方法来创建对象,再使用反射机制通过getter方法来给对象的属性赋值(或者使用setter方法来赋值给对象的属性)。

2、我们也可以让Jackson不使用无参的构造方法来创建对象,这就需要使用注解来告诉Jackson使用带参数的构造方法。

上述的所有注意事项都是针对Jackson的,不一定适用于其他的依赖。

从URL中获取路径参数(接收路径参数)

前端在传递参数时,有很多种方式。可以通过URL传输,也可以通过Body正文传输,甚至还可以手动添加到header中。我们前面学习的接收参数都是通过查询字符串来传输的,但是有些参数是通过URL中不是查询字符串的部分来传输的,而是通过路径参数来传输的。例如,下面这种URL:

https://example.com/users/123

这里的123可能就是用户ID信息,CSDN的个人主页也是采用的这种方式:

我要学编程(ಥ_ಥ)-CSDN博客https://blog.csdn.net/2301_80854132 这个是我的个人主页,后面的数字就是我的个人ID。我们现在就是想要来获取这个参数。

    @RequestMapping("/r11/{id}")
    public String r11(@PathVariable Integer id) {
        return "接收的参数为:"+id;
    }

当我们在Postman中尝试去访问该方法时,就需要输入对应的URL,前面是 r11,后面是的 id 只能是数字。如下所示:

注意:URL中 r11/{id} 中的 id 就是属于路径参数。路径参数的名称是可以随意设置的,这里我们只是设置为了id,也可以设置为其他的,只要保持方法的参数列表中的名称与路径参数的名称一致即可。

上述只是一个路径参数的接收,也可以接收多个路径参数。

    @RequestMapping("/r12/{id}/{user}")
    public String r12(@PathVariable Integer id, @PathVariable String user) {
        return "接收的参数为:"+id +": "+user;
    }

注意:如果有多个参数,那么需要使用多个注解对参数进行绑定。 

除此之外,我们还可以对参数进行重命名:直接注解后面绑定原始的参数名称即可。

    @RequestMapping("/r13/{id}/{xxx}")
    public String r13(@PathVariable Integer id, @PathVariable("xxx") String user) {
        return "接收的参数为:"+id +": "+user;
    }

注意:这里一定不能设置成非必传参数。这个代码的目的是为了获取URL中的参数,那么前端一定会传递参数,退一万步来说,即使我们设置成了非必传参数的话,在实际传递的过程中,并未传输参数,那么最终就会404,因为URL中所对应的参数是需要{id} 和 {xxx}的,我们不传就会找不到。当然有一种情况是例外:我们在输入请求路径时,刚好有一个是 /r13/{id},这样即使最终没有传递参数,也会自动找到该路径,但是这是画蛇添足了。既然有了单独的 /r13/{id} 来接收了id了,为什么还要将 {xxx} 设置成非必传呢?可以不需要设置成非必传呀!当匹配不到 {xxx} 时,就会匹配另一个呀!所以我们不用也不要将其设置成非必传参数。

接收文件(上传文件)

在现在最火的AI时代,我们有很多问题都可以直接去问AI,而不是选择以往的方式去浏览器中搜索了,AI的处理能力还是很强的。当我们想要阅读一本书籍时,但由于时间关系没有充分的时间去细致的阅读,我们就可以将该书的电子版上传给AI,让其给我们简述本书的大概内容即可。这个过程中就涉及了后端接收文件。

    @RequestMapping("/r14")
    public String r14(@RequestPart("file") MultipartFile file) {
        // 获取文件名称
        String fileName = file.getOriginalFilename();
        return "接收的文件名称为:"+fileName;
    }

我们也可以将文件上传到指定位置(服务器专门用来存放文件的位置) ,像百度网盘,阿里云盘,我们通常会将一些重要的图片或者视频保存在里面。这里就是将用户上传的文件保存到本地服务器上。

    @RequestMapping("/r15")
    public String r15(@RequestPart("file") MultipartFile file) throws IOException {
        // 获取文件名称
        String fileName = file.getOriginalFilename();
        // 上传文件到指定路径(D盘的temp目录下)
        file.transferTo(new File("D:\\temp\\"+fileName));
        System.out.println("文件上传成功"); // 这是输出在控制台的日志上
        return "接收的文件名称为:"+fileName;
    }

获取Cookie/Session

由于HTTP协议是无状态协议("无状态"是指在HTTP协议中,每一次请求都是独立的,服务器不会记录任何关于客户端请求的状态或信息。也就是说,服务器不会保存之前请求的信息,每次请求都被视为一个全新的请求。每个HTTP请求都是独立的,它们之间没有任何关联。),我们就需要通过其他的一些机制来实现请求之间存在关联。例如,当我登录一个网站之后,进行其他的查询搜索,这个登录网站是一个HTTP请求,查询搜索是另一个HTTP请求,对于用户来说,登录之后就可以使用了,但是对于服务器来说,两次请求是毫无关系的,那怎么判断是该用户而不是新用户呢?

用户通过浏览器登录成功之后,服务器就会将我的个人信息存储到本地,并创建一个Session(会话),将Session Id通过响应中的Set-Cookie返回给客户端,客户端就会将这个Cookie存储在本地,并在之后的请求中将Cookie自动附加在HTTP请求头中。这样后续服务器就可以通过Cookie中的Session Id识别出当前用户是谁,知道接下来的操作该怎么进行。

Cookie 和 Session的区别:

1、Cookie 是客户端保存用户信息的一种机制,Session是服务器保存用户信息的一种机制(通常保存在服务器的内存中)。

2、Cookie 和 Session之间主要是通过Session Id关联起来,Session Id 是Cookie 和 Session之间的桥梁。

3、虽然Cookie 和 Session 经常在一起配合使用,但也不是必须配合的。

获取Cookie

获取Cookie的方式不止一种,既可以使用原始的方式,也可以使用Spring封装之后的。 

原始的方式是通过 Servlet 所提供的API 来获取 http 请求中的相关信息。我们先来看一下相关的接口:HttpServletRequest、HttpServletResponse。

我们可以在IDEA上输入这两个接口,并摁住 Ctrl + 鼠标左键,进入这个接口内部观察相关信息。

HttpServletRequest 接口的常用方法:

1、getCookies():返回当前请求中所有的Cookie,如果没有,则返回null。

2、getHeader(String name):返回指定请求头的值,以字符串的形式返回。

3、getSession(boolean create):获取当前会话,如果create为true,则在没有会话时创建一个新的会话;反之,则不创建。

4、getSession():获取当前会话,如果没有会话,则自动创建一个新的会话(相当于是对上一个方法的封装)。

5、getMethod():获取请求方法。

6、getQueryString():获取URL中的查询字符串。

7、getRequestURI():获取请求的URL(不包含查询字符串)。

8、getParameter(String name):获取请求中的参数名为name的值。

上面的只做了解即可,毕竟现在Spring都封装完成了,提供了更加方便的注解给我们使用,因此响应的相关方法,我们就不再学习了。

原始的方式

@RequestMapping("/getCookie")
public String getCookie(HttpServletRequest request) {
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
        for (Cookie cookie : cookies) {
            System.out.println(cookie.getName()+": "+cookie.getValue());
        }
    } else {
        System.out.println("Cookie中没有信息~");
    }
    return "Cookie获取成功~";
}

这里的 HttpServletRequest 在我们需要使用的时候,就可以加上,不需要使用就不用加。 

服务端代码编写完成并启动之后,我们就可以使用Postman来构造Cookie并发送请求给服务端。

使用Postman构造Cookie的两种方式:

1、在Header中添加Cookie的信息:

2、直接添加Cookie的信息:

添加Cookie之后,就可以通过Postman发送请求了。输出的信息就在控制台上面。

注意:使用第二种方式添加Cookie信息时,域名要与我们访问路径的域名一致才行,不然就添加不成功。

接下来,我们学习通过Spring的注解来获取:

@RequestMapping("/getCookie")
public String getCookie(@CookieValue("userName") String userName,
                        @CookieValue("password") String password) {
    if (userName == null || password == null) {
        System.out.println("Cookie信息可能为null");
    } else {
        System.out.println(userName+": "+password);
    }
    return "Cookie获取成功~";
}

Spring框架帮我们获取了Cookie中对应key的Value值,并赋值给了参数列表中的形参。

注意:

1、使用Spring的注解去获取Cookie时,一定要有传输对应的key与Value,否则就会发生400。

当然我们也可以设置默认值:

这样当Cookie中没有对应的key时,就不会发生400了。 

2、通过 使用原始的方式 与 使用Spring注解的方式 获取Cookie,我们可以发现虽然Spring给我们封装了,但是我们在获取Cookie时需要手动写一个一个的参数去获取,非常的麻烦,因此我们在获取Cookie时,还是使用原始的方式比较方便。

Session存储与获取

在客户端与服务器第一次通信的过程中,服务器会创建一个新的Session,并返回Session Id 给客户端。服务器得先创建一个Session对象:

@RequestMapping("/setSession")
public String setSession(HttpServletRequest request) {
    // 这里的原理是根据Session Id去查询对应的Session
    // 如果是无参或者参数为true,没有查询到,就返回新的Session对象
    // 如果参数为false,没有查询到,就会返回null
    HttpSession session = request.getSession(); // 默认创建Session对象
    // HttpSession session = request.getSession(true); // 默认创建Session对象
    if (session != null) {
        session.setAttribute("userName", "zhangsan");
        session.setAttribute("password", "123456");
    }
    return "Session存储成功";
}

当我们通过Postman发送请求后,可以在Cookie中看到对应的Session Id。这个原理就是当客户端第一次与服务器通信时,服务器会创建新的Session,并将Session Id通过请求中的Set Cookie返回给客户端。

上面两种方式都可以观察到Session Id 的值。

存储了Session之后,后续就可以直接去获取Session的值了。

@RequestMapping("/getSession")
public String getSession(HttpServletRequest request) {
    HttpSession session = request.getSession(false);
    if (session != null) {
        return "userName: "+session.getAttribute("userName");
    } else {
        return "用户并未登录~";
    }
}

注意:当我们更新服务端的代码并重新启动程序之后,之前所有的Session信息都会被清除。因为Session默认是存储在内存中的,因此我们需要在访问getSession之前,先访问setSession。 

除了上面的获取Session的方法之外,Spring也封装了注解给我们访问。

@RequestMapping("/getSession")
public String getSession(@SessionAttribute(value = "userName") String userName) {
    if (userName != null) {
        return "userName: "+userName;
    } else {
        return "用户并未登录~";
    }
}

注意:当使用Spring的注解来获取session时,如果session中并不存在注解中的value,请求就会发生错误:400。

如果想要不存在改value时,返回null的话,就需要添加参数:required =  false。

@RequestMapping("/getSession")
public String getSession(@SessionAttribute(value = "userName", required = false) String userName) {
    if (userName != null) {
        return "userName: "+userName;
    } else {
        return "用户并未登录~";
    }
}

注意:这里的required 与 前面的 RequestParam 中的required的含义不一样。SessionAttribute 中的 required = false 是表示即使需要获取的value不存在于session的作用域(session中所有key与value组成的集合),也不会抛异常,而是赋值为null;RequestParam 中的 required = false 表示的是当前参数是非必传参数。

还可以使用Spring MVC内置的对象HttpSession来访问。

@RequestMapping("/getSession")
public String getSession(HttpSession session) {
    // 如果Session的作用域中不存在 key=userName 的value,就会返回null,而不是抛异常
    String userName = (String)session.getAttribute("userName");
    if (userName != null) {
        return "userName: "+userName;
    } else {
        return "用户并未登录~";
    }
}

在上述的获取Cookie 和 Session的过程中,我们发现相比于Spring封装的注解,还是原始的方式比较好用,虽然获取的步骤比较繁琐,但是至少不需要我们去刻意避免特殊情况。虽然我们程序员再使用时会注意,但是最终用户在使用时,并不会注意这些东西,如果直接抛出400的话,可能会让用户的体验变差,因此在开发中,还是使用原始的方式比较好。

获取Header 

原始的获取Header的方式也是从HttpServletRequest中获取:

// 原始的方式获取Header
@RequestMapping("/getHeader")
public String getHeader(HttpServletRequest request) {
    // 获取Header
    String header = request.getHeader("User-Agent");
    return "User-Agent: "+header;
}

也可以使用Spring封装的注解来访问:

@RequestMapping("/getHeader")
public String getHeader(@RequestHeader("User-Agent") String header) {
    return "User-Agent: "+header;
}

我们只需要将要获取属性填入注解内部即可,然后就会将对应的值赋值给形参。 

好啦!本期 初始JavaEE篇 —— Spring Web MVC入门(上)的学习之旅 就到此结束啦!我们下一期再一起学习吧!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2295000.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode—487. 最大连续1的个数 II【中等】Plus

2025每日刷题&#xff08;210&#xff09; Leetcode—487. 最大连续1的个数 II 实现代码 class Solution { public:int findMaxConsecutiveOnes(vector<int>& nums) {int zeros 0;int ans 0;for(int l 0, r 0; r < nums.size(); r) {if(nums[r] 0) {zeros;…

【MySQL】窗口函数详解(概念+练习+实战)

文章目录 前言1. SQL窗口函数 1.1 窗口函数概念1.2 窗口函数语法1.3 常见窗口函数 1.3.1 聚合窗口函数1.3.2 专用窗口函数 1.4 窗口函数性能比较 2. LeetCode 例题 2.1 LeetCode SQL 178&#xff1a;分数排名2.2 LeetCode SQL 184&#xff1a;最高工资2.3 LeetCode SQL 185&am…

前端组件标准化专家Prompt指令的最佳实践

前端组件标准化专家Prompt 提示词可作为项目自定义提示词使用&#xff0c;本次提示词偏向前端开发的使用&#xff0c;如有需要可适当修改关键词和示例 推荐使用 Cursor 中作为自定义指令使用Cline 插件中作为自定义指令使用在力所能及的范围内使用最好的模型&#xff0c;可以…

18爬虫:关于playwright相关内容的学习

1.如何在python中安装playwright 打开pycharm&#xff0c;进入终端&#xff0c;输入如下的2个命令行代码即可自动完成playwright的安装 pip install playwright ——》在python中安装playwright第三方模块 playwright install ——》安装playwright所需的工具插件和所支持的…

docker Error response from daemon: Get “https://registry-1.docker.io/v2/ 的问题处理

docker Error response from daemon: Get "https://registry-1.docker.io/v2/ 的问题处理 最近pull 数据 发现 docker 有如下错误 文章目录 docker Error response from daemon: Get "https://registry-1.docker.io/v2/ 的问题处理报错问题检查网络连接解决方案&…

【Linux系统】线程:线程的优点 / 缺点 / 超线程技术 / 异常 / 用途

1、线程的优点 创建和删除线程代价较小 创建一个新线程的代价要比创建一个新进程小得多&#xff0c;删除代价也小。这种说法主要基于以下几个方面&#xff1a; &#xff08;1&#xff09;资源共享 内存空间&#xff1a;每个进程都有自己独立的内存空间&#xff0c;包括代码段…

123,【7】 buuctf web [极客大挑战 2019]Secret File

进入靶场 太熟悉了&#xff0c;有种回家的感觉 查看源代码&#xff0c;发现一个紫色文件 点下看看 点secret 信息被隐藏了 要么源代码&#xff0c;要么抓包 源代码没有&#xff0c;抓包 自己点击时只能看到1和3处的文件&#xff0c;点击1后直接跳转3&#xff0c;根本不出…

微服务知识——微服务拆分规范

文章目录 一、微服务拆分规范1、高内聚、低耦合2、服务拆分正交性原则3、服务拆分层级最多三层4、服务粒度适中、演进式拆分5、避免环形依赖、双向依赖6、通用化接口设计&#xff0c;减少定制化设计7、接口设计需要严格保证兼容性8、将串行调用改为并行调用&#xff0c;或者异步…

双目标定与生成深度图

基于C#联合Halcon实现双目标定整体效果 一&#xff0c;标定 1&#xff0c;标定前准备工作 &#xff08;获取描述文件与获取相机参数&#xff09; 针对标准标定板可以直接调用官方提供描述文件&#xff0c;也可以自己生成描述文件后用PS文件打印 2&#xff0c;相机标定 &…

在 Navicat 17 中扩展 PostgreSQL 数据类型 | 创建自定义域

定义域 以适当的格式存储数据可以确保数据完整性&#xff0c;防止错误&#xff0c;优化性能&#xff0c;并通过实施验证规则和支持高效数据管理来维护系统间的一致性。基于这些原因&#xff0c;顶级关系数据库&#xff08;如PostgreSQL&#xff09;提供了多种数据类型。此外&a…

Linux+Docer 容器化部署之 Shell 语法入门篇 【Shell 替代】

&#x1f380;&#x1f380;Shell语法入门篇 系列篇 &#x1f380;&#x1f380; LinuxDocer 容器化部署之 Shell 语法入门篇 【准备阶段】LinuxDocer 容器化部署之 Shell 语法入门篇 【Shell变量】LinuxDocer 容器化部署之 Shell 语法入门篇 【Shell数组与函数】LinuxDocer 容…

IDEA+DeepSeek让Java开发起飞

1.获取DeepSeek秘钥 登录DeepSeek官网 : https://www.deepseek.com/ 进入API开放平台&#xff0c;第一次需要注册一个账号 进去之后需要创建一个API KEY&#xff0c;然后把APIkey记录保存下来 接着我们获取DeepSeek的API对话接口地址&#xff0c;点击左边的&#xff1a;接口…

mysql的原理及经验

1. 存储引擎 存储引擎是MySQL的核心组件之一&#xff0c;它负责数据的存储和检索。MySQL支持多种存储引擎&#xff0c;每种引擎都有其独特的特点和适用场景。 InnoDB&#xff1a;这是MySQL的默认存储引擎&#xff0c;支持事务处理&#xff08;ACID特性&#xff09;、行级锁定和…

【漫话机器学习系列】083.安斯库姆四重奏(Anscombe‘s Quartet)

安斯库姆四重奏&#xff08;Anscombes Quartet&#xff09; 1. 什么是安斯库姆四重奏&#xff1f; 安斯库姆四重奏&#xff08;Anscombes Quartet&#xff09;是一组由统计学家弗朗西斯安斯库姆&#xff08;Francis Anscombe&#xff09; 在 1973 年 提出的 四组数据集。它们…

e2studio开发RA2E1(9)----定时器GPT配置输入捕获

e2studio开发RA2E1.9--定时器GPT配置输入捕获 概述视频教学样品申请硬件准备参考程序源码下载选择计时器时钟源UART配置UART属性配置设置e2studio堆栈e2studio的重定向printf设置R_SCI_UART_Open()函数原型回调函数user_uart_callback ()printf输出重定向到串口定时器输入捕获配…

开源安全一站式构建!开启企业开源治理新篇章

在如今信息技术日新月异、飞速发展的数字化时代&#xff0c;开源技术如同一股强劲的东风&#xff0c;为企业创新注入了源源不断的活力&#xff0c;然而&#xff0c;正如一枚硬币有正反两面&#xff0c;开源技术的广泛应用亦伴随着不容忽视的挑战。安全风险如影随形&#xff0c;…

Node.js 与 npm 版本兼容性问题详解:如何避免版本冲突

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

鸿蒙开发中 SaveButton 按钮 保存按钮点击后权限授权失败。

原因分析 查看官方文档的解释 在 控制台中 过滤这个字段 过滤关键字"SecurityComponentCheckFail"可以获取具体原因。 得到 产生的原因 是 因为层叠的原因 savebutton 组件必须的 在屏幕的最高层 不能有任何的覆盖和遮挡 通过这样书写就解决了 // 下面是安…

胜任力冰山模型:深入探索职业能力的多维结构

目录 1、序言 2、什么是胜任力&#xff1f; 3、任职资格和胜任力的区别 4、胜任力冰山模型&#xff1a;职场能力的多维展现 4.1、冰山水面上的部分 4.2、冰山水面下的部分 4.3、深层的个人特质与价值观 5、如何平衡任职资格与胜任能力 6、结语 1、序言 在快速发展的I…

C#面试常考随笔12:游戏开发中常用的设计模式【C#面试题(中级篇)补充】

C#面试题&#xff08;中级篇&#xff09;&#xff0c;详细讲解&#xff0c;帮助你深刻理解&#xff0c;拒绝背话术&#xff01;-CSDN博客 简单工厂模式 优点&#xff1a; 根据条件有工厂类直接创建具体的产品 客户端无需知道具体的对象名字&#xff0c;可以通过配置文件创建…