SpringMVC接收和传递数据
一、方法参数
SpringMVC中处理控制器参数的接口是HandlerMethodArgumentResolver,可以有多种具体实现处理不同类型的参数,
例如RequestParamMethodArgumentResolver等
方法的参数可以从下面的8种中进行任意选择,而且不要求顺序,而且参数之间不存在互斥问题
1、接收普通请求参数基本数据类型,另外还有数组String[]、集合类型等
@GetMapping
public ModelAndView sayHello(String name,Integer num){}
使用请求参数名称和方法名称对应的方式接收数据,同时SpringMVC支持自动执行数据类型转换
2、javabean接收http参数【非常重要,有人将javabean称作POJO】,这里在执行服务器端数据校验时需要紧邻一个Error或者BindingResult
类型参数用于存储校验的报错信息,这个是要求顺序的
@GetMapping("/hello.do")
public ModelAndView sayHello(User user){
logger.debug("接收的数据:"+user);
ModelAndView mv=new ModelAndView();
mv.setViewName("hello");//设置跳转页的逻辑地址名,依靠组件InternalResourceViewResolver将逻辑地址名转换为物理地址,同时构建对应的View对象
mv.addObject("msg",new Date());//request.setAttribute("msg",数据);
return mv;
}
3、使用url传递参数,在RESTful使用
4、传递json参数,在RESTful使用
5、MultipartFile处理文件上传,对应CommonsMultipartResolver文件上传解析器
6、Model参数【重要】,主要用于实现通过request域给页面传递数据,具体Model接口的实现由SpringMVC框架负责创建并注入到方法中,可以使用Model、Map和ModelMap
参数Map、Model、ModelMap和request一样,都使用请求域进行数据传递。当然页面跳转必须是请求转发
@GetMapping("/hello.do")
public ModelAndView sayHello(User user, Model model){
logger.debug("接收的数据:"+user);
model.addAttribute("msg",new Date());
return ...;
}
7、另外还有其他参数主要包括请求头、Cookie等
@CookieValue是cookie数据到处理器功能处理方法的方法参数上的绑定
@RequestHeader请求头header数据到处理器功能处理方法的方法参数上的绑定
类似于@RequestParam,只是RequestParam表示数据来源于请求参数,
CookieValue表示数据来源于cookie,RequestHeader表示数据来源于请求头
public ModelAndView sayHello(@RequestHeader(“Accept”)String accept, @CookieValue(“JSESSIONID”) String sessionId,
User user, ModelMap model){
logger.debug(“接收的数据:”+user);
logger.debug(“请求头中名称为Accept对应的值”+accept);
logger.debug(“cookie中存储的sessionId值为”+sessionId);
8、Servlet相关API对象:HttpServletRequest、HttpServletResponse和HttpSession和SpringMVC提供的包装类 [一般不会使用,特殊情况下例如需要获取客户端的IP地址]
@GetMapping("/hello.do")
public ModelAndView sayHello(HttpServletRequest request, HttpServletResponse response, HttpSession session, Model model){
logger.debug("request对象:"+request);
logger.debug("response对象:"+response);
logger.debug("session对象:"+session);
logger.debug("application对象:"+request.getServletContext());
ModelAndView mv=new ModelAndView();
mv.setViewName("hello");//设置跳转页的逻辑地址名,依靠组件InternalResourceViewResolver将逻辑地址名转换为物理地址,同时构建对应的View对象
model.addAttribute("msg",new Date());
return mv;
}
@GetMapping("/hello.do")
public ModelAndView sayHello(ServletWebRequest request,HttpServletResponse response,Model model)
二、方法返回值
1、ModelAndView
通过ModelAndView构造方法可以指定返回的页面名称,也可以通过setViewName()方法跳转到指定的页面
@GetMapping("/hello.do")
public ModelAndView sayHello(Model model){
logger.debug("request对象:"+request);// 由SpringMVC框架提供的包装类,可以提供ServletAPI中request的所有方法
ModelAndView mv=new ModelAndView();
mv.setViewName("hello");//设置跳转页的逻辑地址名,依靠组件InternalResourceViewResolver将逻辑地址名转换为物理地址,同时构建对应的View对象
return mv;
}
2、String表示需要跳转的目标逻辑地址名称。【非常重要】
@GetMapping("/hello.do")
public String sayHello(Model model){
model.addAttribute("msg",new Date());
return "hello";
}
3、Object表示跳转的目标地址就是当前请求的地址名,返回的object就是传递的数据
@ModelAttribute("user") 在当前类中的任何被映射的方法执行之前必须执行的方法,并将返回数据存储在model中,value属性值就是存储的key值
public User user(){
return new User();
}
4、void
@GetMapping("/hello.do") //访问当前方法的URL地址为/aaa/hello.do,返回值为void,则默认跳转地址和URL访问地址相关,跳转目标为/WEB-INF/content/aaa/hello.jsp
public void sayHello(Model model)
5、其他类型Model、ModelMap和Map、View
特殊字符串使用关键字forward或者redirect进行页面跳转
forward一般用于请求转发到其他的控制器方法上
@GetMapping(“/hello.do”)
public String sayHello(Model model){
System.out.println(“hello…”);
model.addAttribute(“msg”,new Date());
return “forward:show.do”; //请求转发到show.do
}
@GetMapping(“/show.do”)
public String show(){
return “hello”;
}
redirect一般用于重定向到其他控制器方法上
@GetMapping("/hello.do")
public String sayHello(Model model){
System.out.println("hello........");
model.addAttribute("msg",new Date());
return "redirect:show.do";
}
@GetMapping("/show.do")
public String show(){
return "hello";
}
总结:
方法的定义要求参数一般是POJO用于接收客户端提交数据、Model用于实现传递数据到jsp页面
方法的返回值一般都是String类型,是跳转页面的逻辑地址名。特殊关键字redirect:
三、常见注解
@Controller等价于Component,供context:component-scan将其转换受管bean
属性value是String类型,用于定义受管bean的名称
@RequestMapping用于实现方法和URL地址之间的映射,通过URL地址可以访问对应的方法。具体实现依赖于SpringMVC框架中的2个组件HandlerMapping和HandlerAdapter。这个注解可以用于方法和类上,如果在类上有这个注解,则方法对应的访问地址为【类上对应的value+方法上对应的value】两部分构成
属性value是String类型,用于定义URL地址的一部分,是path属性的别名
@RequestMapping(“/hello.do”)
属性method是RequestMethod类型,用于设置对应请求的请求方法,可选值有GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE八种请求方法,一般使用GET或者Post,另外支持RESTful时再加上put和delete
@RequestMapping(value = “/hello.do”,method = RequestMethod.POST)
属性params是数组类型,用于针对请求参数提出要求
@RequestMapping(value = “/hello.do”,method = RequestMethod.GET,params = “action”)要求请求参数必须有action,至于action是否有值则没有要求
@RequestMapping(value = “/hello.do”,method = RequestMethod.GET,params = “action=abc”)要求请求参数种必须有action,而且值必须为abc,否则不处理HTTP ERROR 400
同时还有!=之类的写法,例如!action表示不允许有参数action,action!=abc表示可以有参数action,但是值不能为abc;也可以没有action参数
属性headers用于针对请求头添加额外条件,例如是否有某个请求头等
属性consumes和produces一般用于特殊处理请求
属性name用于针对映射添加名称,一般不使用
针对请求方法另外有注解@GetMapping等价于@RequestMapping(method = RequestMethod.GET),@PostMapping等价@RequestMapping(method =
RequestMethod.POST)、@DeleteMapping和@PutMapping
@CookieValue是cookie数据到处理器功能处理方法的方法参数上的绑定
@RequestHeader请求头header数据到处理器功能处理方法的方法参数上的绑定
相关的配置
<!-- 自动注册基于注解风格的处理器 -->
<mvc:annotation-driven/>
@SessionAttributes(“userInfo”)如果model中存储了名称为userInfo的数据,则数据会自动存储到session
四、日期类型数据处理
日期处理是请求参数处理中最复杂的,因为一般日期处理的逻辑不是通用的,过多的定制化处理导致很难有一个统一的标准处理逻辑去处理和转换日期类型的参数。
1、自定义编码处理
2、自定义注册特定数据类型转换器
3、使用@DateTimeFormat或者@JsonFormat
@DateTimeFormat(pattern = “yyyy-MM-dd HH:mm:ss”)
private Date birthday;
4、Jackson序列化和反序列化定制
因为SpringMVC默认使用Jackson处理@RequestBody的参数转换,因此可以通过定制序列化器和反序列化器来实现日期类型的转换
在具体的开发中一般不再建议使用后缀,例如hello.do。目前开发中一般不使用后缀,所以首先修改web.xml
<servlet>
<servlet-name>yan</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>映射路径为/表示当前应用下的所有内容都需要经过前端控制器的处理
<servlet-name>yan</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
定义欢迎首页
@Controller
@RequestMapping("/")
public class IndexController {
@RequestMapping(value ="")
public String index(){
return "index";
}
}
欢迎首页index.jsp
<a href="users/tologin">登录系统</a>
控制器类定义
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping("/tologin")
public String login(){
return "user/login";
}
}
SpringMVC为了简化jsp编程引入了2个标签库
专门用于form表单的标签库
<%@ taglib prefix=“form” uri=“http://www.springframework.org/tags/form” %>
用于jsp页面显示逻辑控制的标签库
<%@ taglib prefix=“spring” uri=“http://www.springframework.org/tags” %>
由于JSTL中的核心标签库可以提供比spring标签库更多的功能,所以一般不使用spring标签库,而是使用JSTL的c标签库
form标签库
1、form:form标签用于生成一个form表单控制,并可以配合其他标签实现报错信息的显示和数据回填操作
<form:form action="users/login" method="post"> action就是提交的URL地址,method就是提交方法
</form:form>
注意form:form要求需要绑定到一个命令对象。
首先修改控制器,通过model参数传递一个名称为user的对象
@GetMapping("/tologin")
public String login(Model model){
User user=new User();
user.setUsername("柏嘉芙");
model.addAttribute("user",user);
return "user/login";
}
修改页面, 在form:form标签中添加了属性modelAttribute,其中的命令对象名称对应于前一个控制器中的model的key值
<form:form action="users/login" method="post" modelAttribute="user">
<table>
<tr>
<td><form:label path="username">用户名称:</form:label></td>
<td><form:input path="username"/></td>
</tr>
<tr>
<td><form:label path="password">用户口令:</form:label></td>
<td><form:password path="password"/></td>
</tr>
2、form:label标签用于生成html中的label标签
<form:label path="username">用户名称:</form:label>
3、form:input标签用于生成html中的单行输入域标签
<form:input path="username"/>
4、form:password标签用于生成html中的单行密码域标签
<form:password path="password"/>
在控制器中新增方法接收post提交数据
@Controller
@RequestMapping("/users")
public class UserController {
@GetMapping("/tologin")
public String login(Model model){
User user=new User();
user.setUsername("柏嘉芙");
model.addAttribute("user",user);
return "user/login";
}
@PostMapping("/login")
public String login(User user){
System.out.println("login..."+user);
return "redirect:show";
}
@GetMapping("show")
public String show(){
return "user/show";
}
}
5、form:radiobutton用于渲染生成一个html单选输入框
<form:radiobutton path="sex" label="男" value="true"/>
<form:radiobutton path="sex" label="女" value="false"/>
form:radiobuttons用于渲染生成一组html单选输入框
基础使用:
首先修改控制器新增方法
@ModelAttribute(“sexOptions”)
public Map<Boolean,String> sexOptions(){
Map<Boolean,String> res=new LinkedHashMap<>();
res.put(true,“男”);
res.put(false,“女”);
return res;
}
修改页面
<form:radiobuttons path="sex" items="${sexOptions}"/>以key为提交数据,以value为显示数据
依靠SpringMVC提供的组件可以实现模型驱动,按照名称对应接收所有提交数据
接收数据后则需要进行数据校验:
1、客户端数据校验。项目中,通常使用较多是前端的校验,比如页面中js校验。对于安全要求较高点建议在服务端进行校验。可以依靠html5提供的三种校验或者自定义js函数进行数据校验
优势:不需要网络数据传输、不会对服务器造成压力
缺点:安全性差,可以很简单的绕过
2、服务求端数据校验,服务器端数据校验一般和客户端数据校验一致,只是为了提高数据校验的安全可靠性。一般在控制器接收数据后采用java编码实现
优势:安全性到,不容易绕过
缺点:需要网络数据传输、会对服务器造成压力
3、业务验证。在调用业务方法时按照业务规则进行数据校验
SpringMVC可以通过hibernate提供的验证框架,使用注解的方式针对客户端提交数据进行校验
使用步骤:
1、添加依赖: hibernate-validator
2、在…-servlet.xml中添加配置数据校验的校验器bean
<bean id="validatorFactory" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"
p:providerClass="org.hibernate.validator.HibernateValidator"/>
3、在mvc注解驱动中配置使用validatorFactory
<mvc:annotation-driven validator="validatorFactory"/>
在调用controller方法之后,马上让数据校验bean开始工作,对数据进行校验
4、在需要执行验证的实体类上添加对应校验规则
@Data
public class User implements Serializable {
private Long id;
@NotBlank(message = "用户名称不允许为空") // 不允许为空,其中message是报错信息
@Length(min = 6,max = 20,message = "用户名称应该是6到20个字符之间") //字符串进行长度校验
private String username;
5、在需要校验的方法参数上添加注解@Validated。如果需要进行数据校验,则值bean后应该紧邻一个参数Error或者BindingResult,这个参数用于存储对应的报错信息
@PostMapping(“/login”)
public String login(@Validated User user, BindingResult errors){ //也可以是Errors类型
//如果有报错信息,则请求转发到输入页面。要求在输入页上进行报错信息
if(errors.hasErrors()){
return “user/login”;
}
6、在输入页面中添加<form:errors path="username"/>
用于显示和username输入域对应的报错信息
7、因为在一个属性上添加了多个验证,则发现有可能出现空值验证没有成功,又执行了长度验证针对这个问题引入分组,通过分组顺序实现验证顺序,前一个验证通过后一个验证才会执行,否则前一个验证失败后一个验证则不执行
public interface UserGroup {
//logingroup分组中包含两个子分组LoginFirstGroup和LoginSecondGroup,执行顺序就是这里声明的顺序
@GroupSequence({LoginFirstGroup.class,LoginSecondGroup.class})
public interface LoginGroup{}
public interface LoginFirstGroup{}
public interface LoginSecondGroup{}
}
8、修改在实体类属性上定义的验证规则,在注解上添加group属性设置所属于的分组
@NotBlank(message = “用户名称不允许为空”,groups = UserGroup.LoginFirstGroup.class) // 不允许为空,其中message是报错信息
@Length(min = 6,max = 20,message = “用户名称应该是6到20个字符之间”,groups = UserGroup.LoginSecondGroup.class) //字符串进行长度校验
private String username;
9、修改控制器方法参数上的注解说明需要执行的分组
public String login(@Validated(UserGroup.LoginGroup.class) User user, BindingResult errors)