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

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

个人主页:我要学编程程(ಥ_ಥ)-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方法@Overridepublic String toString() {return "UserInfo{" +"name='" + name + '\'' +", gender='" + gender + '\'' +", age=" + age +'}';}
}

在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) { // 将数组映射到Listreturn "接收的参数为:"+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项目中集成的就是JackJson。

下面就来演示一下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博客 这个是我的个人主页,后面的数字就是我的个人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,没有查询到,就会返回nullHttpSession 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) {// 获取HeaderString 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.rhkb.cn/news/8765.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python -m pip和pip的主要区别

python -m pip和pip的主要区别在于它们与Python环境的关联方式和安装路径。‌ ‌与Python环境的关联方式‌&#xff1a; pip 是直接使用命令行工具来安装Python包&#xff0c;不指定特定的Python解释器。如果系统中存在多个Python版本&#xff0c;可能会导致安装的包被安装到…

golang通过AutoMigrate方法自动创建table详解

一.AutoMigrate介绍 1.介绍 在 Go 语言中&#xff0c;GORM支持Migration特性&#xff0c;支持根据Go Struct结构自动生成对应的表结构,使用 GORM ORM 库的 AutoMigrate 方法可以自动创建数据库表&#xff0c;确保数据库结构与定义的模型结构一致。AutoMigrate 方法非常方便&am…

SuperAGI - 构建、管理和运行 AI Agent

文章目录 一、关于 SuperAGI&#x1f4a1;特点&#x1f6e0; 工具包 二、⚙️安装☁️SuperAGI云&#x1f5a5;️本地&#x1f300; Digital Ocean 三、架构1、SuperAGI 架构2、代理架构3、代理工作流架构4、Tools 架构5、ER图 一、关于 SuperAGI SuperAGI 一个开发优先的开源…

CSAPP学习:前言

前言 本书简称CS&#xff1a;APP。 背景知识 一些基础的C语言知识 如何阅读 Do-做系统 在真正的系统上解决具体的问题&#xff0c;或是编写和运行程序。 章节 2025-1-27 个人认为如下章节将会对学习408中的操作系统与计算机组成原理提供帮助&#xff0c;于是先凭借记忆将其简单…

如何实现滑动删除功能

文章目录 1 概念介绍2 使用方法3 示例代码 我们在上一章回中介绍了GestureDetector Widget相关的内容,本章回中将介绍Dismissible Widget.闲话休提&#xff0c;让我们一起Talk Flutter吧。 1 概念介绍 我们在这里介绍的Dismissible是一个事件响应Widget,它和GestureDetector类…

【数据结构】_链表经典算法OJ:环形链表的约瑟夫问题

目录 1. 题目链接及描述 2. 解题思路 3. 程序 1. 题目链接及描述 题目链接&#xff1a;环形链表的约瑟夫问题_牛客题霸_牛客网 题目描述&#xff1a; 编号为 1 到 n 的 n 个人围成一圈。从编号为 1 的人开始报数&#xff0c;报到 m 的人离开。 下一个人继续从 1 开始报数…

装饰SpringMVC的适配器实现响应自动包装

文章目录 1.common-tool-starter1.目录结构2.ResultWrapper.java 2.common-web-starter1.目录结构2.IgnoredResultWrapper.java 自定义注解&#xff0c;忽略对返回结果的自动包装3.ReturnValueHandlersDecorator.java 对适配器进行扩展的装饰器4.WebAutoConfiguration.java 将装…

【PyQt5】数据库连接失败: Driver not loaded Driver not loaded

报错内容如下&#xff1a; 可以看到目前所支持的数据库驱动仅有[‘QSQLITE’, ‘QMARIADB’, ‘QODBC’, ‘QODBC3’, ‘QPSQL’, ‘QPSQL7’] 我在网上查找半天解决方法未果&#xff0c;其中有一篇看评论反馈是可以使用的&#xff0c;但是PyQt5的版本有点低&#xff0c;5.12…

论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(三)

Understanding Diffusion Models: A Unified Perspective&#xff08;三&#xff09; 文章概括 文章概括 引用&#xff1a; article{luo2022understanding,title{Understanding diffusion models: A unified perspective},author{Luo, Calvin},journal{arXiv preprint arXiv:…

【shell工具】编写一个批量扫描IP地址的shell脚本

批量扫描某个网段中的主机&#xff08;并发&#xff09; 创建目录编写脚本文件 mkdir /root/ip_scan_shell/ touch /root/ip_scan_shell/online_server.txt touch /root/ip_scan_shell/offline_server.txt touch /root/ip_scan_shell/ip_scan.sh写入下面shell到脚本文件中…

穷举vs暴搜vs深搜vs回溯vs剪枝系列一>解数独

题目&#xff1a; 解析&#xff1a; 部分决策树&#xff1a; 代码设计&剪枝&回溯&#xff1a; 代码&#xff1a; class Solution {private boolean[][] row, col;private boolean[][][] gird; public void solveSudoku(char[][] board) {//下标->数字&#xff…

[EAI-023] FAST: Efficient Action Tokenization for Vision-Language-Action Models

Paper Card 论文标题&#xff1a;FAST: Efficient Action Tokenization for Vision-Language-Action Models 论文作者&#xff1a;Karl Pertsch, Kyle Stachowicz, Brian Ichter, Danny Driess, Suraj Nair, Quan Vuong, Oier Mees, Chelsea Finn, Sergey Levine 论文链接&…

人工智能学习框架:深入解析与实战指南

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 引言 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;深度学习、强化学习和自然语言处理等领域的应用愈加广…

数据结构(树)

每一个节点包含&#xff1a;父节点地址 值 左子节点地址 右子节点地址 如果一个节点不含有&#xff1a;父节点地址或左子节点地址 右子节点地址就记为null 二叉树 度&#xff1a;每一个节点的子节点数量 二叉树中&#xff0c;任意节点的度<2 树的结构&#xff1a; 二叉查…

TikTok 推出了一款 IDE,用于快速构建 AI 应用

字节跳动(TikTok 的母公司)刚刚推出了一款名为 Trae 的新集成开发环境(IDE)。 Trae 基于 Visual Studio Code(VS Code)构建,继承了这个熟悉的平台,并加入了 AI 工具,帮助开发者更快、更轻松地构建应用——有时甚至无需编写任何代码。 如果你之前使用过 Cursor AI,T…

2025多目标优化创新路径汇总

多目标优化是当下非常热门且有前景的方向&#xff01;作为AI领域的核心技术之一&#xff0c;其专注于解决多个相互冲突的目标的协同优化问题&#xff0c;核心理念是寻找一组“不完美但均衡”的“帕累托最优解”。在实际中&#xff0c;几乎处处都有它的身影。 但随着需求场景的…

项目升级Sass版本或升级Element Plus版本遇到的问题

项目升级Sass版本或升级Element Plus版本遇到的问题 如果项目有需求需要用到高版本的Element Plus组件&#xff0c;则需要升级相对应的sass版本&#xff0c;Element 文档中有提示&#xff0c;2.8.5及以后得版本&#xff0c;sass最低支持的版本为1.79.0&#xff0c;所升级sass、…

机器学习第一道菜(二):玩转最小二乘法

机器学习第一道菜&#xff08;二&#xff09;&#xff1a;玩转最小二乘法 一、线性函数表达式1.1 函数表达式 y y y1.2 函数表达式 f θ ( x ) f_\theta(x) fθ​(x)1.3 最小误差 二、最小二乘法&#xff1a;数据拟合大师2.1 概念及其历史背景2.2 拟合优势2.3 数学表达式2.3.1 …

关于低代码技术架构的思考

我们经常会看到很多低代码系统的技术架构图&#xff0c;而且经常看不懂。是因为技术架构图没有画好&#xff0c;还是因为技术不够先进&#xff0c;有时候往往都不是。 比如下图&#xff1a; 一个开发者&#xff0c;看到的视角往往都是技术层面&#xff0c;你给用户讲React18、M…

Python嵌套循环

# coding: utf-8 print("—————————— 嵌套循环 ——————————")while 表达式1&#xff1a;while 表达式2&#xff1a;语句块2for 循环变量1 in 遍历对象1&#xff1a;for 循环变量2 in 遍历对象2&#xff1a;语句块2 print("—————————…