SpringMVC的重定向与转发
1.使用String作为返回值的时候,直接在字符串前添加
redirect:表示当前字符串为重定向的请求路径
forward:表示为转发的请求路径
@Controller
@RequestMapping ( "/test" )
public class MyController {
@RequestMapping ( "/register.do" )
public String doSome ( String name, int age, Model model) {
System. out. println ( "------------" ) ;
model. addAttribute ( "name" , name) ;
model. addAttribute ( "age" , age) ;
return "redirect:other.do" ;
}
@RequestMapping ( "/other.do" )
public String doOther ( Model model) {
return "/welcome.jsp" ;
}
}
2.使用ModelAndView作为返回值时使用setViewName方法跳转
mView.setViewName("/welcome.jsp");默认是请求转发
@RequestMapping ( "/register.do" )
public ModelAndView doAjax ( ) {
System. out. println ( "------------" ) ;
ModelAndView mView = new ModelAndView ( ) ;
mView. addObject ( "name" , "zd" ) ;
mView. addObject ( "age" , 23 ) ;
mView. setViewName ( "redirect:/welcome.jsp" ) ;
return mView;
}
处理器方法的参数
处理器方法参数有5类,在系统调用时自动赋值
1.HttpServletRequest
2.HttpServletResponse
3.HttpSession
4.用于承载数据的Model
5.请求中携带的请求参数
@RequestMapping ( "/register.do" )
public String doSome ( HttpServletRequest request, HttpServletResponse response, HttpSession session, Model model, String name, int age) {
return "redirect:other.do" ;
}
处理器将返回数据添加到响应中
1)处理器返回一个数值类型的数据到前端
直接在处理器中返回一个数据而不是视图名称
需要在方法上添加@ResponseBody注解将返回的数据放入响应体中
< script type = " text/javascript" >
$ ( function ( ) {
$ ( "button" ) . click ( function ( ) {
$. ajax ( {
url : "test/register.do" ,
success: function ( data) {
alert ( data) ;
}
} )
} )
}
)
</ script>
</ head>
< body>
< button > 提交AJAX请求</ button>
</ body>
接收请求后返回一个数值型对象
@ResponseBody注解将返回的数据放入响应体中,需要注册 MVC注解驱动
< mvc: annotation-driven />
不再是返回资源视图名称,直接返回数据到前端
@Controller
@RequestMapping ( "/test" )
public class MyController {
@RequestMapping ( "/register.do" )
@ResponseBody
public Object doAjax ( ) {
System. out. println ( "------------" ) ;
return 123.456 ;
}
}
2)返回String类型对象
对于中文的字符串会出现乱码问题
需要在@RequestMapping标签中设置返回结果的类型
produces="text/html;charset=utf-8"
@Controller
@RequestMapping ( "/test" )
public class MyController {
@RequestMapping ( value= "/register.do" , produces= "text/html;charset=utf-8" )
@ResponseBody
public Object doAjax ( ) {
System. out. println ( "------------" ) ;
return "abc北京" ;
}
}
3)返回自定义类型对象
@Controller
@RequestMapping ( "/test" )
public class MyController {
@RequestMapping ( "/register.do" )
@ResponseBody
public Object doAjax ( ) {
System. out. println ( "------------" ) ;
return new Student ( "张三" ,23 ) ;
}
}
前端的回调函数
success: function ( data) {
alert ( data. name+ " " + data. age) ;
}
4)返回List对象
@RequestMapping ( "/register.do" )
@ResponseBody
public Object doAjax ( ) {
return List< Student> student;
}
前端接收返回结果的回调函数
success: function ( data) {
$( data) . each ( function ( index) {
alert ( data[ index] . name + " " + data[ index] . age) ;
} ) ;
}
5)返回map对象
@RequestMapping ( "/register.do" )
@ResponseBody
public Object doAjax ( ) {
System. out. println ( "------------" ) ;
Map< String, Student> map = new HashMap < String, Student> ( ) ;
Student student = new Student ( ) ;
student. setAge ( 23 ) ;
student. setName ( "张三" ) ;
map. put ( "stu" , student) ;
return map;
}
回调函数解析结果
success: function ( data) {
alert ( data. stu. name + " " + data. stu. age) ;
}
JSON与SpringMVC的交互
1.接收json格式的数据
2.返回json格式的数据
< script type = " text/javascript" >
$ ( function ( ) {
var params = '{"id": 1,"name": "商品","price": 99.9,}' ;
$. ajax ( {
url : "${pageContext.request.contextPath }/json.do" ,
data : params,
contentType : "application/json;charset=UTF-8" ,
type : "post" ,
dataType : "json" ,
success : function ( data) {
alert ( data. name) ;
}
} ) ;
} ) ;
</ script>
处理接收json数据并返回json数据
@RequestMapping ( value = "/json.do" )
@ResponseBody
public Article jsonDate ( @RequestBody Article article) {
System. out. println ( article) ;
return article;
}
SpringMVC的异常处理
SpringMVC对异常处理的方式有3种,处理器方法执行过程中出现的异常
1.使用系统自带的异常处理器
2.使用自定义的异常处理器
3.使用异常注解处理
一,使用系统自带的异常处理器
1)使用系统自带的异常处理器
在springmvc的配置文件中注册这个异常处理器SimpleMappingExceptionResolver
< context: component-scan base-package = " com.handlers" />
< mvc: annotation-driven />
< bean class = " org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" >
< property name = " defaultErrorView" value = " /errors/error.jsp" > </ property>
< property name = " exceptionAttribute" value = " ex" > </ property>
</ bean>
当发生异常时,异常信息会返回给中央调度器,中央调度器会调用注册的这个异常处理器处理异常
@RequestMapping ( "/register.do" )
public void do ( String name, int age) throws StudentException {
System. out. println ( "------------" ) ;
int i = 3 / 0 ;
2)发生指定异常时跳转指定的错误页面
public class StudentException extends Exception {
}
public class NameException extends StudentException
public class AgeException extends StudentException
在SpringMVC的配置文件中注册异常处理器
< context: component-scan base-package = " com.handlers" />
< mvc: annotation-driven />
< bean class = " org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" >
< property name = " defaultErrorView" value = " /errors/error.jsp" > </ property>
< property name = " exceptionAttribute" value = " ex" > </ property>
< property name = " exceptionMappings" >
< props>
< prop key = " com.exceptions.NameException" > /errors/nameErroe.jsp </ prop>
< prop key = " com.exceptions.AgeException" > /errors/ageError.jsp</ prop>
</ props>
</ property>
</ bean>
指定异常的抛出
@RequestMapping ( "/register.do" )
public ModelAndView doAjax ( String name, int age) throws StudentException {
System. out. println ( "------------" ) ;
if ( ! "beijing" . equals ( name) ) {
throw new NameException ( "名字不正确" ) ;
}
if ( age > 20 ) {
throw new AgeException ( "年龄不正确" ) ;
}
二,自定义异常处理器
需要实现HandlerExceptionResolver接口
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException ( HttpServletRequest request, HttpServletResponse response, Object handler,
Exception ex) {
ModelAndView mv = new ModelAndView ( ) ;
System. out. println ( "-----------------" ) ;
mv. addObject ( "ex" , ex) ;
mv. setViewName ( "/errors/error.jsp" ) ;
if ( ex instanceof NameException ) {
mv. setViewName ( "/errors/nameErroe.jsp" ) ;
}
if ( ex instanceof AgeException ) {
mv. setViewName ( "/errors/ageError.jsp" ) ;
}
return mv;
}
}
在SpringMVC中注册异常处理器
< context: component-scan base-package = " com.handlers" />
< mvc: annotation-driven />
< bean class = " com.resolver.MyHandlerExceptionResolver" > </ bean>
三,使用注解式异常处理器
需要@ExceptionHandler注解
@ExceptionHandler(NameException.class)//处理自定义异常
@Controller
@RequestMapping ( "/test" )
public class MyController {
@RequestMapping ( "/register.do" )
public ModelAndView doAjax ( String name, int age) throws StudentException {
System. out. println ( "------------" ) ;
if ( ! "beijing" . equals ( name) ) {
throw new NameException ( "名字不正确" ) ;
}
if ( age > 20 ) {
throw new AgeException ( "年龄不正确" ) ;
}
ModelAndView mView = new ModelAndView ( ) ;
return mView;
}
@ExceptionHandler
public ModelAndView handlerException ( Exception ex) {
ModelAndView mv = new ModelAndView ( ) ;
System. out. println ( "-----------------" ) ;
mv. addObject ( "ex" , ex) ;
mv. setViewName ( "/errors/error.jsp" ) ;
return mv;
}
@ExceptionHandler ( NameException. class )
public ModelAndView handlerNameException ( Exception ex) {
ModelAndView mv = new ModelAndView ( ) ;
System. out. println ( "-----------------" ) ;
mv. addObject ( "ex" , ex) ;
mv. setViewName ( "/errors/nameErroe.jsp" ) ;
return mv;
}
@ExceptionHandler ( AgeException. class )
public ModelAndView handlerAgeException1 ( Exception ex) {
ModelAndView mv = new ModelAndView ( ) ;
System. out. println ( "-----------------" ) ;
mv. addObject ( "ex" , ex) ;
mv. setViewName ( "/errors/ageError.jsp" ) ;
return mv;
}
}
需要注册mvc的注解扫描
< context: component-scan base-package = " com.handlers" />
< mvc: annotation-driven />
SpringMVC的文件上传
1.在前端编写一个文件上传的表单
上传文件时form表单需要属性enctype="multipart/form-data"
< form action = " ${pageContext.request.contextPath}/test/upload.do" method = " POST" enctype = " multipart/form-data" >
文件:< input type = " file" name = " img" /> < br/>
< input type = " submit" value = " 上传" />
</ form>
2.用处理器接收请求中的文件数据
获取上传文件相关信息需要jar包
commons-fileupload.jar
commons-io.jar
注意:处理器方法参数类型为MultipartFile,参数名为前端页面的name属性的值
@RequestMapping ( "/upload.do" )
public String doFileUpload ( MultipartFile img, HttpSession session) throws Exception {
if ( img. getSize ( ) > 0 ) {
String path = session. getServletContext ( ) . getRealpath ( "/images" ) ;
String fileName = img. getOriginalFilename ( ) ;
String extension = FilenameUtils. getExtension ( fileName) ;
String newName = UUID. randomUUID ( ) . toString ( ) + "." + extension;
File file = new File ( path, newName) ;
img. transferTo ( file) ;
System. out. println ( "------------" ) ;
return "/welcome.jsp" ;
}
return "/error.jsp" ;
}
3.在mvc的配置文件中注册文件上传的处理器
< context: component-scan base-package = " com.bjpowernode.handlers" />
< bean id = " multipartResolver" class = " org.springframework.web.multipart.commons.CommonsMultipartResolver" >
< property name = " defaultEncoding" value = " utf-8" />
< property name = " maxUploadSize" value = " 1048576" />
< property name = " maxInMemorySize" value = " 10240" />
</ bean>
< bean class = " org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" >
< property name = " defaultErrorView" value = " /error.jsp" > </ property>
</ bean>
上传多个文件
上传多个文件时
< form action = " ${pageContext.request.contextPath}/test/upload.do" method = " POST" enctype = " multipart/form-data" >
文件1:< input type = " file" name = " imgs" /> < br/>
文件2:< input type = " file" name = " imgs" /> < br/>
文件3:< input type = " file" name = " imgs" /> < br/>
< input type = " submit" value = " 上传" />
</ form>
处理器的参数就不是MultipartFile img而是一个数组MultipartFile[ ] imgs
但是mvc不会自动封装为数据,需要注解@RequestParam将获取的文件封装为一个数组
@RequestMapping ( "/upload.do" )
public String doFileUpload ( @RequestParam MultipartFile[ ] imgs) throws Exception {
String path= "E:\\Java" ;
String fileName= img. getOriginalFilename ( ) ;
File file = new File ( path, fileName) ;
img. transferTo ( file) ;
System. out. println ( "------------" ) ;
return "/welcome.jsp" ;
}
Multipart请求的源码分析
文件上传时注册的类的id值是DispatcherServlet类的属性值之一
< bean id = " multipartResolver" class = " org.springframework.web.multipart.commons.CommonsMultipartResolver" >
DispatcherServlet类的属性
public static final String MULTIPART_RESOLVER_BEAN_NAME = "multipartResolver" ;
部分代码
try {
this . multipartResolver = context. getBean ( MULTIPART_RESOLVER_BEAN_NAME, MultipartResolver. class ) ;
if ( logger. isDebugEnabled ( ) ) {
logger. debug ( "Using MultipartResolver [" + this . multipartResolver + "]" ) ;
}
}
protected void doDispatch ( HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false ;
WebAsyncManager asyncManager = WebAsyncUtils. getAsyncManager ( request) ;
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart ( request) ;
multipartRequestParsed = ( processedRequest != request) ;
if ( multipartRequestParsed) {
cleanupMultipart ( processedRequest) ;
}
protected HttpServletRequest checkMultipart ( HttpServletRequest request) throws MultipartException {
if ( this . multipartResolver != null && this . multipartResolver. isMultipart ( request) ) {
if ( WebUtils. getNativeRequest ( request, MultipartHttpServletRequest. class ) != null) {
logger. debug ( "Request is already a MultipartHttpServletRequest - if not in a forward, " +
"this typically results from an additional MultipartFilter in web.xml" ) ;
}
else if ( request. getAttribute ( WebUtils. ERROR_EXCEPTION_ATTRIBUTE) instanceof MultipartException ) {
logger. debug ( "Multipart resolution failed for current request before - " +
"skipping re-resolution for undisturbed error rendering" ) ;
}
else {
return this . multipartResolver. resolveMultipart ( request) ;
}
}
return request;
}
SpringMVC的类型转换器
在页面表单提交的无论是int还是double类型数据,都能在处理接收之前转换为相应类型的数据
这是因为SpringMVC中有默认的类型转换器
SpringMVC中没有日期类型的转换器,因为日期格式太多,有一种默认的日期格式,2018/9/9——年/月/日 ,不支持前端发送的年-月-日的格式
自定义类型转换器
1.需要实现Converter接口,重写convert方法
Converter<S, T >有2个泛型
S:源的类型,表示在页面接收的数据类型
T:宿的类型,想要转换的类型
public class MyConverter implements Converter < String, Date> {
@Override
public Date convert ( String source) {
SimpleDateFormat sdf = new SimpleDateFormat ( "yyyy-MM-dd" ) ;
try {
return sdf. parse ( source) ;
} catch ( ParseException e) {
e. printStackTrace ( ) ;
}
return null;
}
}
2.注册自定义的类型转换器
< context: component-scan base-package = " com.handlers" />
< bean id = " myConverter" class = " com.handlers.MyConverter" />
< bean id = " conversionService" class = " org.springframework.context.support.ConversionServiceFactoryBean" >
< property name = " converters" >
< set>
< ref bean = " myConverter" />
</ set>
</ property>
</ bean>
< mvc: annotation-driven conversion-service = " conversionService" />
3.定义一个form表单提交日期格式数据
会自动调用类型转换器将提交的数据转为自定义日期格式的数据
< form action = " ${pageContext.request.contextPath}/test/register.do" method = " POST" >
姓名:< input type = " text" name = " name" />
生日:< input type = " text" name = " birther" />
< input type = " submit" value = " 注册" />
</ form>
4.接收日期格式数据
将请求中字符串格式的日期自动转为Date类型的数据赋值给birther变量
@RequestMapping ( "/register.do" )
public ModelAndView doSome ( String name, Date birther) {
ModelAndView mView = new ModelAndView ( ) ;
mView. addObject ( "name" , name) ;
mView. addObject ( "birther" , birther) ;
mView. setViewName ( "/welcome.jsp" ) ;
return mView;
}
利用正则表达式匹配多种日期格式
自定义的类型转换器可以将String数据可以转换为指定格式的Date数据,
但是原先默认的yyyy/MM/dd格式无法使用
可以使用正则表达式,在一个类型转换器中所有不同格式的String
正则表达式以^开头,以$结尾,\d表示数字 ,\d{4} 表示4位数字
import java.util.regex.Pattern; 导入正则模块
@Override
public Date convert ( String source) {
SimpleDateFormat sdf = getDateFormat ( source) ;
try {
return sdf. parse ( source) ;
} catch ( ParseException e) {
e. printStackTrace ( ) ;
}
return null;
}
private SimpleDateFormat getDateFormat ( String source) {
SimpleDateFormat sdf = null;
if ( Pattern. matches ( "^\\d{4}-\\d{2}-\\d{2}$" , source) ) {
sdf = new SimpleDateFormat ( "yyyy-MM-dd" ) ;
} else if ( Pattern. matches ( "^\\d{4}/\\d{2}/\\d{2}$" , source) ) {
sdf = new SimpleDateFormat ( "yyyy/MM/dd" ) ;
} else if ( Pattern. matches ( "^\\d{4}\\d{2}\\d{2}$" , source) ) {
sdf = new SimpleDateFormat ( "yyyyMMdd" ) ;
}
return sdf ;
}
SpringMVC的类型转换异常
当获取的参数的类型无法转换时会出现TypeMismatchException
这个异常出现在处理器获取参数之前,无法使用自动的异常解析器处理
可以使用注解式的异常解析器
@ExceptionHandler ( TypeMismatchException. class )
public ModelAndView handlerException ( Exception ex) {
ModelAndView mv = new ModelAndView ( ) ;
mv. setViewName ( "/index.jsp" ) ;
return mv;
}
自定义日期格式转换时可能出现的一种异常是ParseException,
这个异常已经被try..catch捕获了,外部无法得知
所有可以在发生ParseException之前主动抛出一个TypeMismatchException
private SimpleDateFormat getDateFormat ( String source) {
SimpleDateFormat sdf = new SimpleDateFormat ( ) ;
if ( Pattern. matches ( "^\\d{4}-\\d{2}-\\d{2}$" , source) ) {
sdf = new SimpleDateFormat ( "yyyy-MM-dd" ) ;
return sdf ;
} else if ( Pattern. matches ( "^\\d{4}/\\d{2}/\\d{2}$" , source) ) {
sdf = new SimpleDateFormat ( "yyyy/MM/dd" ) ;
return sdf ;
} else if ( Pattern. matches ( "^\\d{4}\\d{2}\\d{2}$" , source) ) {
sdf = new SimpleDateFormat ( "yyyyMMdd" ) ;
return sdf ;
} else {
throw new TypeMismatchException ( "" , Date. class ) ;
}
数据的回显和异常信息的提示
用户提交数据出现异常时,
将请求中的参数和详细的异常信息放入ModelAndView对象中返回数据填写页面
@ExceptionHandler ( TypeMismatchException. class )
public ModelAndView handlerException ( HttpServletRequest request, Exception ex) {
String age = request. getParameter ( "age" ) ;
String birthday = request. getParameter ( "birthday " ) ;
String errors = ex. message ( ) ;
ModelAndView mv = new ModelAndView ( ) ;
mView. addObject ( "age " , age ) ;
mView. addObject ( "birthday" , birthday ) ;
if ( errors. contains ( age) ) {
mView. addObject ( "ageError" , "年龄输入有误" ) ;
}
if ( errors. contains ( birthday) ) {
mView. addObject ( "birthdayError" , "日期格式输入有误" ) ;
}
mv. setViewName ( "/index.jsp" ) ;
return mv;
}
回显用户提交的数据
< form action = " ${pageContext.request.contextPath}/test/register.do" method = " POST" >
姓名:< input type = " text" name = " name" value = ${age} /> ${ageError} < br/>
生日:< input type = " text" name = " birther" value = ${birthday} /> ${birthdayError}< br/>
< input type = " submit" value = " 注册" />
</ form>
初始化参数绑定实现类型转换
将字符串格式的日期参数转换为自定义格式的日期类型
利用@InitBinder注解实现类型的转换
利用@InitBinder只能绑定一种类型的日期格式,想要多种就需要自定义属性编辑器
@Controller
@RequestMapping ( "/test" )
public class MyController {
@RequestMapping ( "/register.do" )
public ModelAndView doSome ( String name, Date birther) {
ModelAndView mView = new ModelAndView ( ) ;
mView. addObject ( "name" , name) ;
mView. addObject ( "birther" , birther) ;
mView. setViewName ( "/welcome.jsp" ) ;
return mView;
}
@InitBinder
public void doDate ( WebDataBinder binder) {
DateFormat df = new SimpleDateFormat ( "yyyy-MM-dd" ) ;
binder. registerCustomEditor ( Date. class , new CustomDateEditor ( df, true ) ) ;
}
}
自定义属性编辑器要继承PropertiesEditor类,需要重写setAsText方法
public class MyDateEditor extends PropertiesEditor {
@Override
public void setAsText ( String source) {
SimpleDateFormat sdf = getDateFormat ( source) ;
try {
Date date = sdf. parse ( source) ;
setValue ( date) ;
} catch ( ParseException e) {
e. printStackTrace ( ) ;
}
return null;
}
private SimpleDateFormat getDateFormat ( String source) {
SimpleDateFormat sdf = new SimpleDateFormat ( ) ;
if ( Pattern. matches ( "^\\d{4}-\\d{2}-\\d{2}$" , source) ) {
sdf = new SimpleDateFormat ( "yyyy-MM-dd" ) ;
} else if ( Pattern. matches ( "^\\d{4}/\\d{2}/\\d{2}$" , source) ) {
sdf = new SimpleDateFormat ( "yyyy/MM/dd" ) ;
} else {
throw new TypeMismatchException ( "" , Date. class ) ;
}
return sdf ;
}
}
SpringMVC的数据验证
对前台数据进校验(除非安全性较高的应用,否则一般只在前台做验证即可)
SpringMVC支持JSP 303-Bean Validation数据验证规范
验证的是对象,不是基本数据类型,该规范的实现很多,常用的是Hibernate Validator
classmate.jar
hibernate-validator.Final.jar
validation-api.Final.jar
1.在springmvc,xml中注册验证器
< bean id = " myValidator"
class = " org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" >
< property name = " providerClass" value = " org.hibernate.validator.HibernateValidator" />
</ bean>
< mvc: annotation-driven validator = " myValidator" />
2.在传递参数对应的实体类中编写校验规则
public class Student {
@NotNull ( message= "姓名不能为空" )
@Size ( min= 3 , max= 6 , message= "姓名长度应在{min} —— {max} 间" )
private String name;
@Min ( value= 18 , message= "年龄不能小于{value}" )
@Max ( value= 120 , message= "年龄不能大于{value}" )
private int age;
@NotNull ( message= "手机号不能为空" )
@Pattern ( regexp= "^1[34578]\\d{9}$" , message= "手机号格式不正确" )
private String mobile;
}
3.在处理器中接收数据校验结果
数据到处理器之前会自动封装为Student类型时,使用@Valid注解进行校验,将异常信息封装到BindingResult bResult中
在处理器中获取参数校验结果即可
@RequestMapping ( "/register.do" )
public ModelAndView doSome ( @Valid Student student, BindingResult bResult) {
ModelAndView mView = new ModelAndView ( ) ;
int errorCount = bResult. getErrorCount ( ) ;
if ( errorCount > 0 ) {
FieldError nameError = bResult. getFieldError ( "name" ) ;
FieldError ageError = bResult. getFieldError ( "age" ) ;
FieldError mobileError = bResult. getFieldError ( "mobile" ) ;
if ( nameError != null) {
String name = nameError. getDefaultMessage ( ) ;
mView. addObject ( "names" , name) ;
}
if ( ageError != null) {
String age = ageError. getDefaultMessage ( ) ;
mView. addObject ( "ages" , age) ;
}
if ( mobileError != null) {
String mobile = mobileError. getDefaultMessage ( ) ;
System. out. println ( mobile) ;
mView. addObject ( "mobiles" , mobile) ;
}
}
mView. addObject ( "student" , student) ;
mView. setViewName ( "/index.jsp" ) ;
return mView;
}
4.在前台显示异常信息
< form action = " ${pageContext.request.contextPath}/test/register.do" method = " POST" >
姓名:< input type = " text" name = " name" /> ${ names}< br>
成绩:< input type = " text" name = " age" /> ${ ages}< br>
手机号:< input type = " text" name = " mobile" /> ${mobiles }< br>
< input type = " submit" value = " 注册" />
</ form>