这节的内容很简单,都是SpringMVC Controller的传接参相关内容,贴笔记简单记了。主要是快过年了,工作的心情都没有,更别提学习了,就像卡兹停止了思考一样,我的脑子已经被僵尸吃了;懒癌给爷爬。
课程地址:https://space.bilibili.com/284638819
一、控制器接收Client端请求参数
1、Web开发中传参图解
2、基于Servlet API接受Client请求参数
String name = request.getParameter("name");
String password = request.getParameter("password");
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/param1")
public String param1(HttpServletRequest request, HttpServletResponse response) {
String name = request.getParameter("name");
String password = request.getParameter("password");
return "param1";
}
}
注意:这种方式虽然最为直观、简单、但是代码冗余多且与ServletAPI存在耦合,所以在SpringMVC开发中并不建议使用。
3、基于简单变量接受Client请求参数
所谓简单变量:指的就是8种基本类型+String这些类型的变量。把这些类型的变量,作为控制器方法的形参,用于接受client提交的数据。
@RequestMapping("/param2")
public String param2(String name,String password){
System.out.println("password = " + password);
System.out.println("name = " + name);
return "param2";
}
请求 : http://localhost:8989/springmvc1/param/param2?name=dsdsds&password=dsdsds
细节:常见类型自动转换,比如看注释内容
@RequestMapping("/param3")
//尽量使用包装器,使用int的话传null无法存储从而报错
public String param3(String name,Integer age){
System.out.println("age = " + age);
System.out.println("name = " + name);
return "param2";
}
这么传就会报错,因为int给了null,所以需要上面的包装器Integer:
@Controller
@RequestMapping("/param")
public class ParamController {
@RequestMapping("/param2")
public String param2(String name, int age) {
return "param2";
}
}
http://localhost:8989/springmvc1/param/param2?name=sunshuai
程序报错:原因在于age参数。age在提交数据时没有提交内容,等同于null。而参数中age使用了int类型,是基本类型,无法存储null值,所以产生了错误。
改正方式:
1. age参数使用包装器类型,可以存储null值
2. 为age参数设置默认值,但是需要@RequestParam注解配合使用。
注意:1、常见类型泛指:8种基本类型及其包装器,String等常见类型。2、Date日期等特殊类型,默认不支持,需要程序员自定义类型转换器。
4、基于POJO类型接受Client请求参数
什么是POJO
1. POJO全程叫做Plain Ordinary Java Object(简单的Java对象)
2. POJO类型对象的特点是:
1. 类型中如果存在成员变量,必须提供set get方法。
2. 提供默认无参构造
3. 可以实现Serializable,也可以不实现
4. 不实现容器或者框架所规定的接口
3. 用户根据业务封装的实体、DTO这些类型就是POJO。
使用场景
传简单变量需要传参名和接参的形参名对应,传POJO的则需要传参名和POJO类中的属性名(成员变量名)对应。
@RequestMapping("/param4")
//前端送参,少送实体中有的参数,会补默认值,多送实体中没有的参数,实体不处理,基本类型送空,实体不用包装器定义成员变量,报错,String送空,实体给空
public String param4(User user){
System.out.println(user.toString());
System.out.println("password = " + user.getPassword());
System.out.println("name = " + user.getName());
System.out.println("age = " + user.getAge());
return "param2";
}
<form method="post" action="${pageContext.request.contextPath}/param/param4">
UserName<input type="text" name="name" id="name" /></br>
PassWord<input type="text" name="password" id="password" /></br>
<%-- Age<input type="text" name="age" id="age" /></br>--%>
<%-- PassWord<input type="text" name="pp" id="pp" /></br>--%>
<input type="submit" value="提交">
</form>
注意1:
http://localhost:8989/springmvc1/xxxController?name=sunshuai&password=123456&age=10
@RequestMapping("/param3")
public String param3(String name,User user) {
return "param3";
}
name形参与user对象中的name属性,都会获取对应的内容。这个特点在后续学习前端后端分离开发时,是会使用。
注意2:
1. 如果SpringMVC发现控制器形参类型,是8种基本类型+String的话,他会通过形参名与请求参数的key对应,接受数据。
2. 如果SpringMVC发现控制器形参类型,不是8种基本类型+String的话,他会查找对应形参类型的属性名与请求参数的key对应,接受数据
3. 如果存在自定义类型转换器不适用于上述规律
5、接受一组简单变量的请求参数
使用场景
<form method="post" action="${pageContext.request.contextPath}/param/param6">
<input type="checkbox" name="ids" value="5666" /></br>
<input type="checkbox" name="ids" value="7788" /></br>
<input type="checkbox" name="ids" value="6677" /></br>
<input type="checkbox" name="ids" value="8899" /></br>
<input type="checkbox" name="ids" value="9900" /></br>
<input type="submit" value="提交">
</form>
@RequestMapping("/param5")
public String param5(int[] ids){
for (int id: ids){
System.out.println("id = " + id);
}
return "param2";
}
细节:
@RequestMapping("/param1")
public String param1(List<Integer> ids) {
for (Integer id : ids) {
System.out.println("id = " + id);
}
return "result1";
}
抛出异常:SpringMVC无法提供具体的实现类,实例化形参。
@RequestMapping("/param1")
public String param1(ArrayList<Integer> ids) {
for (Integer id : ids) {
System.out.println("id = " + id);
}
return "result1";
}
没有异常,但是接受不到数据:SpringMVC会按照POJO的匹配方式,进行成员变量查找。
6、接受一组POJO类型对象的请求参数
使用场景
<form method="post" action="${pageContext.request.contextPath}/param/param7">
UserName1<input type="text" name="users[0].name" /></br>
UserPassword1<input type="text" name="users[0].password" /></br>
UserAge1<input type="text" name="users[0].age" /></br>
<hr/>
UserName2<input type="text" name="users[1].name" /></br>
UserPassword2<input type="text" name="users[1].password" /></br>
UserAge2<input type="text" name="users[1].age" /></br>
<input type="submit" value="提交">
</form>
@RequestMapping("/param7")
public String param7(UsersDTO usersDTO){
List<User> users = usersDTO.getUsers();
for (User user : users){
System.out.println("user = " + user);
}
return "param2";
}
public class UsersDTO {
private List<User> users;
public List<User> getUsers() {
return users;
}
public void setUsers(List<User> users) {
this.users = users;
}
}
只需要将user的列表对象作为属性传接参即可,就是users。
7、接受Client请求参数的总结
二、@RequestParam注解
@RequestParam注解:用于修饰控制器方法的形参。
1、解决请求参数与方法形参名字不一致的问题
@RequestMapping("/param1")
public String param1(@RequestParam(value = "n",required = false,defaultValue = "fuckyou") String name,@RequestParam(value = "p") String password){
System.out.println("name = " + name);
System.out.println("password = " + password);
return "param1";
}
这样前端传参的参数属性就是n和p
1、@RequestParam注解简写形式:不书写value属性的内容,@RequestParam默认会把对应形参名作为value属性的值。
2、使用了@RequestParam注解的形参,客户端必须传递数据,不能省略。否则报错。之后可以用require属性解决
3、POJO类型的形参,不能与@RequestParam注解联用。报400错误。
//POJO类型的形参,不能与@RequeatParam注解联用
@RequestMapping("/param2")
public String param2(@RequestParam User user){
System.out.println("user.toString() = " + user.toString());
return "param1";
}
4、应用场景
@RequestMapping("/param3")
public String param3(@RequestParam("id<") Integer id){
System.out.println("id = " + id);
return "param1";
}
2、@RequestParam的required属性
1.在使用@RequestParam注解时,可以应用required属性。
required = true时:@RequestParam修饰的控制器方法参数,客户端必须提交数据,否则报错,默认值
required = false时:@RequestParam修饰的控制器方法参数,客户端可以传递数,也可以不传递。
2.解决使用@RequestParam注解,客户端必须传递数据的问题。
3、@RequestParam的defaultValue属性
客户端没有提交数据的时候,给对应的形参提供默认值
@RequestMapping("/param6")
public String param6(@RequestParam String name,@RequestParam(defaultValue = "0") int age){
System.out.println("age = " + age);
System.out.println("name = " + name);
return "param1";
}
注意:如果使用了defaultValue,则required属性也默认被设置成了false
解决控制器方法形参,使用包装器的问题,毕竟有默认值了。
defaultValue典型的使用场景
默认值操作,一个典型的应用场景是:分页首页查询,不传页号的设计
http://localhost:8989/ems/employee/findAllEmployees
http://localhost:8989/ems/employee/findAllEmployees?pageNum=2
http://localhost:8989/ems/employee/findAllEmployees?pageNum=3
@RequestMapping("/findAllEmployees")
public String findAllEmployees(Model model, @RequestParam(value = "pageNum",defaultValue = "1") Integer pageNum)
三、中文请求参数的乱码问题
略
四、SpringMVC的类型转换器
1、内置类型转换器的概念
1.SpringMVC提供了内置类型转换器,把客户端提交的字符串类型的请求参数,转换成控制器方法参数需要的数据类型。
2.SpringMVC并不是对于所有的类型,都提供了内置的类型转换器,他只是提供了常见类型的转换器
比如:8种基本类型,常见的集合类型等
2、原理
在SpringMVC启动时,会通过<mvc:annotation-driven/> 把FormattingConversionServiceFactoryBean。引入到SpringMVC体系中。FormattingConversionServiceFactoryBean存储了SpringMVC中所有的内置类型转换器。后续client提交请求参数时,如果对应控制器方法形参不是字符串类型,那么FormattingConversionServiceFactoryBean就会调用对应的类型转化器,进行类型转换,最终完成控制器方法形参的赋值。
3、SpringMVC中自定义类型转换器
SpringMVC在接受客户端提交请求参数时,如果请求参数对应的控制器方法形参,是非常规数据类型,SpringMVC默认情况下无法进行类型转换。会抛出异常,程序员可以通过自定义类型转换器解决上述问题。
如:日期类型
如何加上自定义类型转换器,只需要写自定义转换类对象,然后注入到FormattingConversionServiceFactoryBean即可。
package com.jin;
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author jinyunlong
* @date 2022/1/29 10:52
* @profession ICBC锅炉房保安
*/
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
Date parse = null;
try {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
parse = simpleDateFormat.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return parse;
}
}
<!--引入SpringMVC的核心功能-->
<mvc:annotation-driven conversion-service="serviceFactoryBean"/>
<bean id="dateConverter" class="com.jin.DateConverter">
</bean>
<bean id="serviceFactoryBean" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
<property name="converters">
<set>
<ref bean="dateConverter"></ref>
</set>
</property>
</bean>
<form method="post" action="${pageContext.request.contextPath}/editor/editor2">
UserName<input type="text" name="name" id="name" /></br>
UserBrithday<input type="text" name="birthday" id="birthday" /></br>
<%-- Age<input type="text" name="age" id="age" /></br>--%>
<%-- PassWord<input type="text" name="pp" id="pp" /></br>--%>
<input type="submit" value="提交">
</form>
@RequestMapping("/editor2")
public String editor2(String name, Date birthday){
System.out.println("name = " + name);
System.out.println("birthday = " + birthday);
return "param1";
}
五、接受其他请求数据
1、动态参数收集
单值动态参数收集
@RequestMapping("/param1")
public String param1(@RequestParam Map<String,String> params){
Set<String> keys = params.keySet();
for(String key : keys){
System.out.println("key = " + key);
System.out.println("params = " + params.get(key));
}
return "param1";
}
注意:如果需要接受动态参数,必须保证Map的形参前面加入@RequestParam注解,否则接受不到数据。
多值动态参数收集
@RequestMapping("/param2")
public String param2(@RequestParam MultiValueMap<String,String> params){
Set<String> keys = params.keySet();
for(String key : keys){
List<String> values = params.get(key);
System.out.println("key = " + key);
for (String value : values){
System.out.println("value = " + value);
}
}
return "param1";
}
多值动态参数收集典型使用场景: 排序:可以简化设计↓
单值和多值动态参数收集分别对应一个简单变量和一组简单变量传接参。
2、接受cookie数据
Servlet中获取的方式
#Cookie创建的代码
Cookie cookie = new Cookie("name","value");
#核心代码
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies) {
if ("cookieName".equals(cookie.getName())) {
System.out.println("cookie.getValue() = " + cookie.getValue());
}
}
SpringMVC中获取Cookie
@RequestMapping("/other1")
public String other1(HttpServletRequest request){
Cookie[] cookies = request.getCookies();
for (Cookie cookie : cookies){
if("name".equals(cookie.getName())){
System.out.println("cookie.getValue() = " + cookie.getValue());
}
}
return "param1";
}
@RequestMapping("/other2")
public String other2(@CookieValue("name") String cookieValue){
System.out.println("cookieValue = " + cookieValue);
return "param1";
}
第一种基于Servlet API获取Cookie,存在与Servlet API的耦合问题,还是别用了。。
3、接受请求头数据
1.Http协议规定:客户端浏览器在发起请求时,除了提交数据外,还会通过请求头向服务器端提交一些额外的附加息。
比如语言信息,浏览器的版本,客户端操作系统类型,是否缓存数据等信息。
2.在后续的开发中,特殊场景下,甚至会自定义请求头携带一些特殊数据。比如jwt token
Servlet中请求头的获取方式
String value = request.getHeader("key");
SpringMVC中请求头的获取方式
@RequestMapping("/other3")
public String other3(HttpServletRequest request){
String host = request.getHeader("host");
System.out.println("host = " + host);
return "param1";
}
@RequestMapping("/other4")
public String other4(@RequestHeader("host") String host){
System.out.println("host = " + host);
return "param1";
}
和获取Cookie一样,第一种有耦合,不好,还是用注解比较好