如果还没有阅读前面的笔记,可以点击下面的链接
springMVC学习笔记(1),开发第一个springMVC的web应用,包含实例的详细步骤和截图
springMVC学习笔记(2),包括三层架构与SSM的关系,springMVC中MVC模式的实现,配置视图解析器。
处理器方法的参数
我们要想获得用户输入的信息,或请求中所携带的其他参数,可以从处理器方法的参数中获得,这些参数会在用户发起请求时,前端控制器分配到指定处理器方法时(即框架系统自动调用该方法),由系统为这些参数赋值。程序员只需要在方法中直接使用这些参数即可。
处理器方法可以包含以下四类参数:
- HttpServletRequst
- HttpServletResponse
- HttpSession
- 请求中所携带的请求参数
- 逐个接收
- 对象接收
这些参数就是处理器方法的形式参数,在定义处理器方法时,需要使用哪种获得参数方式,就在形式参数中定义哪些参数,这些参数的值,会在调用时,由系统自动按一定的规则赋值。
public ModelAndView doSome(形式参数列表){
}
例如:
public ModelAndView doSome(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
HttpSession httpSession){
}
下面代码,我们来在处理器方法中从HttpServletRequest参数中获得请求中的携带的参数name的值
@RequestMapping(value = "/some.do")
public ModelAndView doSome(HttpServletRequest request,
HttpServletResponse response,
HttpSession session){
ModelAndView mv = new ModelAndView();
mv.addObject("msg","欢迎使用SpringMVC" + request.getParameter("name"));
mv.addObject("fun","执行的是doSome方法");
mv.setViewName("show");
return mv;
}
我们在地址栏中,直接输入请求,并通过?号来携带参数name=testname
逐个参数接收
我们也可以直接使用请求中参数的名称作为处理器方法的形参。要求:
- 形参名与请求中参数的名称一致
- 顺序没有要求
- 类型可以是任意基本数据类型,请求参数默认都是使用String类型传递,如果形参使用其他参数类型,框架会在自动赋值时,进行类型转换,如果不能转换成功,则会出错,处理器方法将得不到执行。
例如使用以下代码来接收请求中的多个参数值
@RequestMapping(value = "/some.do")
public ModelAndView doSome(String name,int age){
ModelAndView mv = new ModelAndView();
mv.addObject("name",name);
mv.addObject("age",age);
mv.setViewName("show");
return mv;
}
因为我们修改了Model中的数据,所以,在View(show.jsp)中,也相应的修改一下获取Model值的代码
<h2>show.jsp从request域中获取数据</h2>
<h3>姓名:${name}</h3>
<h3>年龄:${age}</h3>
测试结果:
当然,如果我们采用form进行POST提交,处理方法是一样的。这里就不做演示了。
但是,我们可能会遇到以下问题:
- 参数值为空
- 参数无法自动转换类型
- 值为中文时为乱码
首先,当参数值为空时,如果你的形参类型为String类型,空是允许的,只是值为空字符串而以。但是如果当形参为如int类型时,Tomcat会报错误
400错误码,表示请求参数类型不匹配。原因是,在处理器方法的形式参数中,我们定义的是int age
所以,系统在为形参自动赋值时,会将值自动按形参类型转型,但是,因为值为空,空值无法转型为int
类型,所以,报错。这时候,在Tomcat的日志上,其实会显示异常信息
org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type ‘java.lang.String’ to required type ‘int’; nested exception is java.lang.NumberFormatException: For input string: “”
MethodArgumentTypeMismatchException是方法参数类型不匹配异常。
这时,我们可以将形式参数类型设置成为int
的包装类型Integer
,那么当参数值为空时,会被转型为null,不会报错。
public ModelAndView doSome(String name,Integer age){
}
测试结果:
但是,如果你的形参类型无论为int
还是Integer
,当参数得到的值为字母或其他字符,还是会出现方法参数类型不匹配异常。
有两种解决办法:
- 在前端对数据类型和是否为空进行检测,这也是常用办法
- 申明形式参数类型为String,然后在处理器方法中对数据进行检测和处理。但这其实还是和框架帮你进行类型转换是差不多的。所以,并不建议。
请求参数中文乱码问题
我们现在试试在请求中提交中文。
可以看出,当使用GET方法提交请求时,中文处理没有问题。但是如果我们改用POST方法进行提交呢?
下面在index.jsp中增加表单,使用POST方法来提交请求
<form action="user/some.do" method="post">
姓名:<input type="text" name="name" /><br/>
年龄:<input type="text" name="age" /><br/>
<button type="submit" >提交</button>
</form>
点击“提交”按钮后,结果为:
可以看出,当使用POST方法提交请求时,中文数据出现了乱码。
如果学习过java web开发的同学,应该知道,我们可以在doPost方法中,使用request.setCharacterEncoding(“utf-8”)来指定POST的参数的字符集编码。
public void doPost(HttpServletRequest request) throws UnsupportedEncodingException {
request.setCharacterEncoding("utf-8");
}
但是,这需要为每一个servlet的doPost方法都添加上这一条语句。
实际开发中,我们可以使用过滤器来解决乱码问题。
我们可以直接使用框架中提供的过滤器CharacterEncodingFilter
这个类在以下包中
由于我们已添加了依赖,所以,可以直接使用。
修改web.xml文件,添加注册过滤器申明
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
</filter>
其中,filter-name
可以自定义,但一般设置为类名的首字母小写
CharacterEncodingFilter这个类有三个属性,需要在xml文件中来指定属性值。
我们可以通过Ctrl + 鼠标单击类名,来跟踪查看该类的定义,可以看到类的源码
public class CharacterEncodingFilter extends OncePerRequestFilter {
@Nullable
private String encoding;
private boolean forceRequestEncoding = false;
private boolean forceResponseEncoding = false;
...
}
- encoding:编码名称
- forceRequestEncoding:是否强制请求使用该编码,默认为
false
- forceResponseEncoding:是否强制响应使用该编码,默认为
false
因此,继续修改web.xml文件,设置过滤器的属性值。
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
设置编码为utf-8
,并强制要求request和response都使用该编码。
下面再添加过滤器映射,设置哪些请求将应用这个过滤器,我们是将所有请求都使用这个过滤器。
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
现在,我们设置好了过滤器,/*
表示对所有请求都强制过滤器,即使用utf-8
编码。我们再看一下效果。
可以看到。现在不管是通过GET还是POST请求,中文处理都正常了。
校正请求参数名@RequestParam
前面我们要求,处理器方法的形参名称,要求和请求的参数名称相同。如果因为开发的某些原因,不相同,我们该怎么办。
可以使用@RequestParam注解形参,来指定请求中的参数名称。如下:
<form action="user/some.do" method="post">
姓名:<input type="text" name="myname" /><br/>
年龄:<input type="text" name="myage" /><br/>
<button type="submit" >提交</button>
</form>
现在请求参数名称为myname和myage,但我们处理器参数希望继续保持name和age,我们可以为形参加上@RequestParam注解
public ModelAndView doSome(@RequestParam("myname") String name,
@RequestParam("myage") Integer age){
...
}
@RequestParam注解除了value属性外,还有一个required属性,表示,是否一定要有该参数。默认为true
,这时,如果请求中缺少这个参数,会出现400错误。
当你认为有些参数在提交时,也可以没有时,可以将required属性设置为false
首先,当没有设置required属性时,这个属性值采用默认值true
。
当我们直接在地址栏中输入http://localhost:8080/hello/user/some.do
,发起请求时,不携带任何参数
可以看到,会出现400错误,为缺少请求参数。
下面,我们设置required属性值为false
public ModelAndView doSome(@RequestParam(value = "myname",required = false) String name,
@RequestParam(value = "myage",required = false) Integer age){
...
}
再尝试在地址栏中直接输入请求URL,不携带任何参数
没有报错,只是说没有得到相应的参数值。
对象参数接收
当提交的参数较多时,如果继续使用处理器方法形参来列出所有的参数,会不太方便。
我们可以直接把一个自定义类的对象作为处理器方法的形参。这个类的各属性与提交的参数名称相同,类型也可以相一致,并为这些属性添加getter和setter方法,框架在调用这个处理器方法时,会自动产生一个这个类的对象,并通过这个类的setter方法来将提交的参数的值分别为这个对象的各属性赋值,最后,将这个对象通过处理器方法的形参传给处理器方法。程序员可以直接在方法中使用这个对象,并通过这个对象的getter方法来获得各属性值中保存的提交参数值。
我们需要先定义一个类(VO,value object,值对象),比如你是要添加一个学生,提交的信息是学生信息,我们需要创建一个值对象类Student,其需要提交的参数与Student类的各属性名称和类型相同(主要是需要类的属性名称与请求的参数名称要相同)。
例如,我们需要提交的是学生的name和age
<form action="user/some.do" method="post">
姓名:<input type="text" name="name" /><br/>
年龄:<input type="text" name="age" /><br/>
<button type="submit" >提交</button>
</form>
我们可以定义一个VO类Student类,代码如下:
package com.javaoldman.vo;
public class Student {
private String name;
private int age;
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
为了方便打印对象信息,我们还重写了toString方法。
下面,就可以直接使用Student对象作为处理器方法的形参,并直接在方法中使用该对象来获取到提交的各参数值。前面说过,框架会在调用这个处理器方法前,先自动创建一个Student对象,并将请求中携带的参数按名称通过类的setter方法给对象的属性赋值。再将这个对象传给这个处理器方法。
@RequestMapping(value = "/some.do")
public ModelAndView doSome(Student student){
ModelAndView mv = new ModelAndView();
mv.addObject("student",student);
mv.setViewName("show");
return mv;
}
在View视图文件show.jsp
中,修改相应代码如下:
<h3>姓名:${student.name}</h3>
<h3>年龄:${student.age}</h3>
因为在View视图中,我们可以直接得到Model中的Student对象,名称叫student。所以,可以通过这个对象来访问相应的属性值。
通过对象形参来接收请求参数是我们较常用的方式,特别是当提交的参数个数较多时。
下一节,我们再讨论处理器方法的返回值