看过之前的蜕变系列文章,相信你对springMVC有了一定的认识。对controller的花式编写,也有了一定的认识。今天我们来开启新的认识,讲一讲web开发中比较关键的东西,比如页面跳转,数据类型转换,以及数据校验。
每月底工厂君会根据后台记录筛选转发文章前三位的朋友,给与奖励,第一名100元,第二名50元,第三名30元的现金奖励。行动力超强的你,关注公号,转发文章,赶紧动起来吧!
猿蜕变同样是一个原创系列文章,帮助你从一个普通的小白,开始掌握一些行业内通用的框架技术知识以及锻炼你对系统设计能力的提升,完成属于你的蜕变,更多精彩内容,敬请大家关注公主号猿人工厂,点击猿人养成获取!
随着前后端分离的套路大行其道,可能有的同学可能已经忘记了还有转发和重定向这两回事情了。我们先一起来回忆回忆这两个基础知识,面试常问的送分题。
转发:forward,是指服务器内部的跳转,转发的流程如下:客户端发起请求->服务端接受请求->调用内部的方法转发动作->将转发的资源返回给客户端。
重定向:redirect,是指服务器接受到客户端请求之后,响应302状态给客户端,让客户端对另一个资源(url)发起请求,再次请求的可以是服务器内部资源也可以是外部资源。
那么对于servlet来讲重定向就是调用HttpServletResponse的sendRedirect(“需要重定向的资源名”)方法,转发就是调用HttpServletRequest:request.getRequestDispatcher("需要转发的资源名").forword(request,response);
转发和重定向是两件不同的事情,他们的区别主要是:
1.转发对于客户端来讲是发起一次请求,而重定向则需要客户端发起两次请求。
2.转发时浏览器地址栏不会变,而重定向需要改变浏览器地址。
为了让大家有一个更加清楚的理解springMVC的转发和重定向,我们看下面这个例子:
编写两个新页面分别表示转发和重定向的页面:forward.jsp 和 redirect.jsp
forward.jsp:
<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8"isELIgnored="false"%><html><head><title>forward样例title>head><body> <h1>这是一个forward!浏览器地址不变!h1>body>html>
redirect.jsp:
<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8"isELIgnored="false"%><html><head><title>redirect样例title>head><body> <h1>这是一个redirect!浏览器地址改变!h1>body>html>
package com.pz.web.study.springmvc.controller; import javax.servlet.http.HttpServletRequest;impor tjavax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping; @Controllerpublicclass RedirectAndForwardDemo { @RequestMapping("/forward.do") public String forward(HttpServletRequestrequest,HttpServletResponse response) throws Exception { return"forward:forward.jsp"; } @RequestMapping("/redirect.do") public String redirect(HttpServletRequest request,HttpServletResponse response) throws Exception{ return"redirect:http://127.0.0.1/redirect.jsp"; } }
启动应用,分别访问
http://127.0.0.1/forward.do
http://127.0.0.1/redirect.do
在SpringMVC中使用转发:可以使用方法返回值为String的方法,return “forward:资源名称”,需要注意,一定要写资源相对于webapp目录的完整名称,因为使用转发,之前配置的视图解析器会失效。
在SpringMVC中使用重定向:可以使用方法返回值为String的方法,return “redirect:资源名称”,需要注意,重定向如果在内部可以写相对路径,但是大多数情况下重定向请求的是外部资源,所以例子用的是完整的url。
当然,你还可以使用HttpServetRequest,和HttpServletResponse来做也是可以的,只是这样做没有使用框架特性,就不一一列出了,有兴趣,自己做下体会下就好。
SpringMVC可以将请求里的参数会转化为对应的类型,是因为Spring MVC 提供了默认的类型转换器(converter)来做这些事情,但是默认的类型转换器并不是万能的,比如像日期类型(Date)就不能转换,为了解决个问题我们需要自定义类型转换器来做这些事情。
Spring MVC 提供了org.springframework.core.convert.converter.Converter接口,用于用户自己实现数据类型转换的功能。我们看下接口定义:
public interface Converter<S, T> { /** *Convert the source of type S to target type T. * @param source the source object to convert, whichmust be an instance of S * @return the converted object, which must be aninstance of T * @throws IllegalArgumentException if the source could notbe converted to the desired target type */ T convert(S source); }
可以看出,这个接口是一个泛型接口,S代表转换前的数据类型,T代表转换后的数据类型。接下来我们编写代码实现自定义日期类型转换器:
package com.pz.web.study.springmvc.util; import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.Date; import org.springframework.core.convert.converter.Converter; /** * 自定义日期类型转换器 * 日期格式:yyyy-MM-dd * @author pangzi * */public class SimpleDateConverter implements Converter<String,Date>{ @Override public Date convert(String dateStr) { if (dateStr != null && !"".equals(dateStr)) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try { return sdf.parse(dateStr); } catch (ParseException e) { e.printStackTrace(); } } return null; } }
日期转换器编写好了,我们怎么使用它呢?修改配置文件spring-servlet.xml文件,增加以下内容(注意:下面给出的是完整配置,请观察项目的配置文件):
<bean id="dateConverter"class="com.pz.web.study.springmvc.util.SimpleDateConverter"/> <bean id="conversionService"class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"ref="dateConverter"/> bean> <mvc:annotation-driven conversion-service="conversionService"> <mvc:message-converters register-defaults="true"> <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8value> <value>application/jsonvalue> <value>application/xml;charset=UTF-8value> list> property> <property name="features"> <list> <value>WriteMapNullValuevalue> <value>WriteNullNumberAsZerovalue> <value>WriteNullListAsEmptyvalue> <value>WriteNullStringAsEmptyvalue> <value>WriteNullBooleanAsFalsevalue> <value>WriteDateUseDateFormatvalue> list> property> bean> mvc:message-converters> mvc:annotation-driven>
编写个Controller看看效果
package com.pz.web.study.springmvc.controller; import java.util.Date; import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping; @Controllerpublic class DateConverterControllerDemo { @RequestMapping("/convertDate.do") public String convertDate(String name, String phone,int age,Date birthDay,Model model) throws Exception { model.addAttribute("name", name); model.addAttribute("phone", phone); model.addAttribute("age", age); model.addAttribute("birthDay", birthDay); return"showDateConvertForm"; } }
编写两个页面
dateConvertForm.jsp
<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8"isELIgnored="false"%><html><head><title>spring form表单样例使用对象接收参数title>head><body> <form action="convertDate.do"method=post> <lable>姓名:lable> <input type="text"name="name" id="name"/><br /> <lable>电话:lable> <input type="text"name="phone" id="phone"/><br /> <lable>年龄:lable> <input type="text"name="age" id="age"/><br /> <lable>年龄:lable> <input type="text"name="birthDay" id="birthDay"/><br /> <input type="submit"value="提交"id="submit" /><br /> form>body>html>
showDateConvertForm.jsp
<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8"isELIgnored="false"%><html><head><title>spring form表单样例title>head><body> <lable>姓名:lable> ${name} <br /> <lable>电话:lable> ${phone}<br /> <lable>年龄:lable> ${age}<br /> <lable>生日:lable> ${birthDay}<br />body>html>
启动应用访问
http://127.0.0.1/dateConvertForm.jsp
录入页面信息,点击提交看效果。
当然,Spring MVC 也提供了注解的方式——@DateTimeFormat来解决日期的问题.只不过需要依赖joda time(当然joda time被JDK8版本以后纳入了JDK,如果你是使用JDK8以上版本,可以不依赖joda time)。
接下来,我们使用annotation的方式来处理日期问题:
在pom.xml中增加依赖:
<dependency> <groupId>joda-timegroupId> <artifactId>joda-timeartifactId> <version>2.9version> dependency>
编写Controller代码在DateConverterControllerDemo中新增方法:
@RequestMapping("/convertDateByAnnotation.do") public String convertDateByAnnotation(String name,String phone ,int age,@DateTimeFormat(pattern = "yyyy-MM-dd")Date birthDay,Model model) throws Exception { model.addAttribute("name", name); model.addAttribute("phone", phone); model.addAttribute("age", age); model.addAttribute("birthDay", birthDay); return"showDateConvertForm"; }
编写页面代码:
<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8"isELIgnored="false"%><html><head><title>使用annotation转换日期title>head><body> <form action="convertDateByAnnotation.do"method=post> <lable>姓名:lable> <input type="text"name="name" id="name"/><br /> <lable>电话:lable> <input type="text"name="phone" id="phone"/><br /> <lable>年龄:lable> <input type="text"name="age" id="age"/><br /> <lable>生日:lable> <input type="text"name="birthDay" id="birthDay"/><br /> <input type="submit"value="提交"id="submit" /><br /> form>body>html>
http://127.0.0.1/dateConvertByAnnotationtForm.jsp
录入页面信息,点击提交看效果。
web程序开发嘛,有很多需要做数据校验的地方,比如对页面form表单的校验,我们经常使用JavaScript对form表单的数据进行校验。但是用户通过url的方式拼接或者使用工具随意传入非法参数怎么办(我们之前开发的例子中也有不少)?所以,在后端我们同样需要校验参数。用户的参数校验是一个比较复杂的问题,除了数据格式,还有业务数据校验,这里我们先说说数据格式的问题。给你安利一个叫做hibernate validator的家伙。
Java在JSR303 规范中提出了Bean Validation 规范,这个规范提出主要使用annotation的方式来实现对 Java Bean 的验证功能,这个规范的是实现者很多,其中hibernate的validator实现得比较好,也应用得比较广泛,这里我们主要讲解hibernatevalidator在Spring MVC中的使用。
我们先修改pom.xml增加hibernate validator的依赖:
<dependency>
<groupId>org.hibernategroupId>
<artifactId>hibernate-validatorartifactId>
<version>5.0.2.Finalversion>
dependency>
我们修改下User类增加hibernate-validator的annotation给属性增加一些约束:
package com.pz.web.study.springmvc.domain; importj avax.validation.constraints.Max;import javax.validation.constraints.Min;import javax.validation.constraints.NotNull;import javax.validation.constraints.Pattern;import javax.validation.constraints.Size; publicclass User { @NotNull(message = "请输入姓名") @Size(min = 4,max =20,message = "姓名长度必须在{min}-{max}之间") private String userName; @NotNull(message = "请输入手机号码") @Size(min = 4,max =20,message = "手机号码长度必须在{min}-{max}之间") @Pattern(regexp = "^1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}$", message = "手机号码格式不正确") private String cellPhone; @NotNull(message = "请输入年龄") @Min(value = 0,message = "年龄不能小于{value}") @Max(value =120,message = "年龄不能大于{value}") privateintage; private Address address; public String getUserName() { returnuserName; } publicvoidsetUserName(String userName) { this.userName = userName; } public String getCellPhone() { returncellPhone; } publicvoidsetCellPhone(String cellPhone) { this.cellPhone = cellPhone; } publicint getAge() { returnage; } publicvoid setAge(int age) { this.age = age; } public Address getAddress() { returnaddress; } publicvoidsetAddress(Address address) { this.address = address; } }
修改spring-servlet.xml配置文件,增加hibernate-validator验证器相关配置:
<bean id="hibernateValidator"class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"> <property name="providerClass"value="org.hibernate.validator.HibernateValidator"/>bean> <mvc:annotation-driven conversion-service="conversionService"validator="hibernateValidator"> <mvc:message-converters register-defaults="true"> <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8value> <value>application/jsonvalue> <value>application/xml;charset=UTF-8value> list> property> <property name="features"> <list> <value>WriteMapNullValuevalue> <value>WriteNullNumberAsZerovalue> <value>WriteNullListAsEmptyvalue> <value>WriteNullStringAsEmptyvalue> <value>WriteNullBooleanAsFalsevalue> <value>WriteDateUseDateFormatvalue> list> property> bean> mvc:message-converters> mvc:annotation-driven>
编写Controller,方法中需要User和BindingResult,在需要校验的User前增加@Validaed,我们可以通过BindingResult获取验证错误信息,并用于页面输出:
package com.pz.web.study.springmvc.controller; import java.util.List; import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.validation.BindingResult;import org.springframework.validation.FieldError;import org.springframework.validation.ObjectError;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.annotation.RequestMapping; import com.pz.web.study.springmvc.domain.User; @Controllerpublic class HibernateValidatorControllerDemo { @RequestMapping("/validateDataForm.do") public String validateDataForm(@Validated User user,BindingResult bingResult,Modelmodel) throws Exception { List allErrors= bingResult.getAllErrors(); if (allErrors != null && allErrors.size() > 0) { FieldError nameError =bingResult.getFieldError("userName"); FieldError ageError =bingResult.getFieldError("age"); FieldError phoneError =bingResult.getFieldError("cellPhone"); if (nameError != null) { model.addAttribute("nameError", nameError.getDefaultMessage()); } if (ageError != null) { model.addAttribute("ageError", ageError.getDefaultMessage()); } if (phoneError != null) { model.addAttribute("phoneError", phoneError.getDefaultMessage()); } return"../validateDataForm"; } model.addAttribute("user", user); return"showDateConvertForm"; } }
编写两个jsp页面
validateDataForm.jsp
<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8"isELIgnored="false"%><html><head><title>spring form Hibernate-Validator校验表单title>head><body> <form action="/validateDataForm.do"method=post> <lable>姓名:lable> <input type="text"name="userName" id="userName"/> ${nameError}<br /> <lable>电话:lable> <input type="text"name="cellPhone" id="cellPhone"/> ${phoneError}<br /> <lable>年龄:lable> <input type="text"name="age" id="age"/> ${ageError}<br /> <input type="submit"value="提交" id="submit"/><br /> form>body>html>
showValidateDataForm.jsp
<%@ page language="java" contentType="text/html;charset=utf-8" pageEncoding="utf-8"isELIgnored="false"%><html><head><title>spring form Hibernate-Validator校验表单title>head><body> <lable>姓名:lable> ${user.userName} <br /> <lable>电话:lable> ${user.cellPhone}<br /> <lable>年龄:lable> ${user.age}<br /> <lable>省:lable> ${user.address.provice} <br /> body>html>
启动应用输入
http://127.0.0.1/validateDataForm.jsp
查看页面效果。
HibernateValidator 中的常用验证注解
@Null被注释的元素必须为 null
@NotNull 被注释的元素必须不为 null
@AssertTrue 被注释的元素必须为 true
@AssertFalse 被注释的元素必须为 false
@Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max,min) 被注释的元素的大小必须在指定的范围内
@Digits(integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past 被注释的元素必须是一个过去的日期
@Future 被注释的元素必须是一个将来的日期
@Pattern(value) 被注释的元素必须符合指定的正则表达式
被注释的元素必须是电子邮箱地址 | |
@Length(min=, max=) | 被注释的字符串的大小必须在指定的范围内 |
@NotEmpty | 被注释的字符串的必须非空 |
@Range(min=, max=) | 被注释的元素必须在合适的范围内 |
我建了一个群,群里有很多高手,欢迎大家入群探讨。