“SpringMVC:高级应用”的版本间差异

来自Wikioe
跳到导航 跳到搜索
第406行: 第406行:


== 异常处理器 ==
== 异常处理器 ==
springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。<br/>
处理思路:<br/>
* 系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。
[[File:springMVC异常处理.png|500px]]
=== 自定义异常 ===
为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。
<syntaxhighlight lang="java">
public class CustomException extends Exception {
/** serialVersionUID*/
private static final long serialVersionUID = -5212079010855161498L;
public CustomException(String message){
super(message);
this.message = message;
}
//异常信息
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
</syntaxhighlight>
=== 自定义异常处理器 ===
<syntaxhighlight lang="java">
public class CustomExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
ex.printStackTrace();
CustomException customException = null;
//如果抛出的是系统自定义异常则直接转换
if(ex instanceof CustomException){
customException = (CustomException)ex;
}else{
//如果抛出的不是系统自定义异常则重新构造一个未知错误异常。
customException = new CustomException("未知错误,请与系统管理 员联系!");
}
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("message", customException.getMessage());
modelAndView.setViewName("error");
return modelAndView;
}
}
</syntaxhighlight>
=== 错误页面 ===
error.jsp:
<syntaxhighlight lang="java">
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>错误页面</title>
</head>
<body>
您的操作出现错误如下:<br/>
${message }
</body>
</html>
</syntaxhighlight>
=== 配置异常处理器 ===
springmvc.xml中添加
<syntaxhighlight lang="xml">
<!-- 异常处理器 -->
<bean id="handlerExceptionResolver" class="cn.itcast.ssm.controller.exceptionResolver.CustomExceptionResolver"/>
</syntaxhighlight>
=== 异常测试 ===
如:修改controller方法“editItem”,调用service查询商品信息,如果商品信息为空则抛出异常。
<syntaxhighlight lang="java">
// 调用service查询商品信息
Items item = itemService.findItemById(id);
if(item == null){
throw new CustomException("商品信息不存在!");
}
</syntaxhighlight>


== 上传图片 ==
== 上传图片 ==

2020年10月5日 (一) 08:29的版本


Validation

对提交的请求数据进行检验。

依赖包

  • hibernate-validator-4.3.0.Final.jar
  • jboss-logging-3.1.0.CR2.jar
  • validation-api-1.0.0.GA.jar

配置validator

springmvc.xml

Converter配置
使用<mvc:annotation-driven> 使用HandlerAdapter
<mvc:annotation-driven validator="validator"></mvc:annotation-driven>
<!-- 注解适配器 -->
<bean
	class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
	<property name="webBindingInitializer" ref="customBinder"></property>
</bean>

<!-- 自定义webBinder -->
<bean id="customBinder"
	class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer">
	<property name="validator" ref="validator" />
</bean>
<!-- 校验器 -->
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
	<!-- hibernate校验器-->
	<property name="providerClass" value="org.hibernate.validator.HibernateValidator" />
	<!-- 指定校验使用的资源文件,在文件中配置校验错误信息,如果不指定则默认使用classpath下的ValidationMessages.properties -->
	<property name="validationMessageSource" ref="messageSource" />
</bean>

<!-- 校验错误信息配置文件 -->
<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
	<!-- 资源文件名-->
	<property name="basenames">   
		<list>    
			<value>classpath:CustomValidationMessages</value> 
		</list>   
	</property>
	<!-- 资源文件编码格式 -->
	<!-- <property name="defaultEncoding" value="UTF-8"></property> -->
	<property name="fileEncodings" value="UTF-8" />
	<!-- 对资源文件内容缓存时间,单位秒 -->
	<property name="cacheSeconds" value="120" />
</bean>

CustomValidationMessages.properties

位置参考<value>classpath:CustomValidationMessages</value>,内容如:

#添加校验错误提交信息
items.name.length.error=请输入1到30个字符的商品名称
items.createtime.isNUll=请输入商品的生产日期
item.price.isNull=test,价格不能为空
items.message.test=校验信息配置测试文字

使用validator

添加验证规则

在pojo的属性之上添加验证规则:@NotNull(message="{item.price.isNull}",groups= {ValidGroup1.class}),其中:

  1. @NotNull:校验名称;
  2. message="{item.price.isNull}":错误信息(配置于“CustomValidationMessages.properties”);
  3. groups= {ValidGroup1.class}:此校验所属的分组;
  4. @NotNull:;
public class Items {
    private Integer id;

    //校验名称在1到30字符中间
    //message是提示校验出错显示的信息
    //groups:此校验属于哪个分组,groups可以定义多个分组
    @Size(min=1,max=30,message="{items.message.test}",groups={ValidGroup1.class})
    private String name;

    @NotNull(message="{item.price.isNull}",groups= {ValidGroup1.class})
    private Float price;

    private String pic;

    //非空校验
    @NotNull(message="{items.createtime.isNUll}",groups= {ValidGroup1.class})
    private Date createtime;

    private String detail;

    //get、set、toString...
}

错误消息文件

验证规则中配置的message="{item.price.isNull}",对应于“CustomValidationMessages.properties”中的信息:

#添加校验错误提交信息
items.name.length.error=请输入1到30个字符的商品名称
items.createtime.isNUll=请输入商品的生产日期
item.price.isNull=test,价格不能为空
items.message.test=校验信息配置测试文字
  • 如果在eclipse中编辑properties文件无法看到中文则参考“Eclipse开发环境配置-indigo.docx”添加propedit插件。【???】

捕获错误

在Controller的方法形参前添加@Validated来在参数绑定时进行校验,并将校验信息写入BindingResult中:

  1. 在要校验的pojo后边添加BingdingResult
  2. 一个BindingResult对应一个pojo;
// 商品信息修改提交
// 在需要校验的pojo前边添加@Validated,在需要校验的pojo后边添加BindingResult
// bindingResult接收校验出错信息
// 注意:@Validated和BindingResult bindingResult是配对出现,并且形参顺序是固定的(一前一后)。
// value={ValidGroup1.class}指定使用ValidGroup1分组的 校验
// @ModelAttribute可以指定pojo回显到页面在request中的key
@RequestMapping("/editItemsSubmit")
public String editItemsSubmit(
		Model model,
		HttpServletRequest request,
		Integer id,
		@ModelAttribute("items") @Validated(value = { ValidGroup1.class}) ItemsCustom itemsCustom,
		BindingResult bindingResult,
		MultipartFile items_pic//接收商品图片
		) throws Exception {

	// 获取校验错误信息
	if (bindingResult.hasErrors()) {
		// 输出错误信息
		List<ObjectError> allErrors = bindingResult.getAllErrors();

		for (ObjectError objectError : allErrors) {
			// 输出错误信息
			System.out.println(objectError.getDefaultMessage());

		}
		// 将错误信息传到页面
		model.addAttribute("allErrors", allErrors);
		
		// 可以直接使用model将提交pojo回显到页面
		model.addAttribute("items", itemsCustom);
		
		// 出错重新到商品修改页面
		return "items/editItems";
	}
	
	//原始名称
	String originalFilename = items_pic.getOriginalFilename();
	//上传图片
	if(items_pic!=null && originalFilename!=null && originalFilename.length()>0){
		//存储图片的物理路径
		String pic_path = "F:\\develop\\upload\\temp\\";
		//新的图片名称
		String newFileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
		//新图片
		File newFile = new File(pic_path+newFileName);
		//将内存中的数据写入磁盘
		items_pic.transferTo(newFile);
		//将新图片名称写到itemsCustom中
		itemsCustom.setPic(newFileName);
	}
	
	// 调用service更新商品信息,页面需要将商品信息传到此方法
	itemsService.updateItems(id, itemsCustom);

	return "success";
}

错误回显

在捕获错误后,页面需要显示错误信息。
如:

  1. 页头:
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  
    <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
    
  2. 在需要显示错误信息地方:
    <spring:hasBindErrors name="item">
    	<c:forEach items="${errors.allErrors}" var="error">
    		${error.defaultMessage }<br/>
    	</c:forEach>
    </spring:hasBindErrors>
    
    • <spring:hasBindErrors name="item">表示如果item参数绑定校验错误下边显示错误信息。

或,如:【???????????】

  1. Controller:
    	if(bindingResult.hasErrors()){
    		model.addAttribute("errors", bindingResult);
    	}
    
  2. 在需要显示错误信息地方:
    <c:forEach items="${errors.allErrors}" var="error">
    	${error.defaultMessage }<br/>
    </c:forEach>
    

分组校验

如果两处校验使用同一个Items类则可以设定校验分组,通过分组校验可以对每处的校验个性化。

定义分组

分组就是一个标识,可以使用一个空接口来进行分组定义。

package cn.itcast.ssm.controller.validation;

public interface ValidGroup1 {
	//接口中不需要定义任何方法,仅是对不同的校验规则进行分组
	//此分组只校验商品名称长度
}
package cn.itcast.ssm.controller.validation;

public interface ValidGroup2 {
	//接口中不需要定义任何方法,仅是对不同的校验规则进行分组
   //此分组只校验xxxxxx
}

指定分组

在pojo的校验规则中使用groups= {ValidGroup1.class}来指定所属的分组。

@Size(min=1,max=30,message="{items.message.test}",groups={ValidGroup1.class})
private String name;

@NotNull(message="{item.price.isNull}",groups= {ValidGroup1.class})
private Float price;

@NotNull(message="{items.createtime.isNUll}",groups= {ValidGroup1.class})
private Date createtime;

使用分组校验

在Controller中使用@Validated(value={ValidGroup1.class})来使用ValidGroup1分组的检验规则。

// 商品修改提交
@RequestMapping("/editItemSubmit")
public String editItemSubmit(
			@Validated(value={ValidGroup1.class}) @ModelAttribute("item") Items items,
			BindingResult result,
			@RequestParam("pictureFile") MultipartFile[] pictureFile,
			Model model) throws Exception {
			
			...
}
  • 也可以使用逗号分隔来指定多个分组:@Validated(value={ValidGroup1.classValidGroup2.class })

校验注解

校验 说明
@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(regex=,flag=) 被注释的元素必须符合指定的正则表达式
Hibernate Validator 附加的 constraint
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内

数据回显

数据回显:表单提交失败,回到表单填写页面时,将原提交的数据重新显示在页面中。

简单类型

简单的数据类型的回显,使用Model将传入的参数添加到request域即可。

@RequestMapping(value="/editItems",method={RequestMethod.GET})
public String editItems(Model model,Integer id)throws Exception{
		
	//传入的id重新放到request域
	model.addAttribute("id", id);

	...
}

pojo类型

  1. 默认回显:pojo数据传入controller方法后,springmvc自动将pojo数据放到request域,;
    @RequestMapping("/editItemSubmit")
    public String editItemSubmit(Integer id,ItemsCustom itemsCustom)throws Exception{}
    
    <tr>
    	<td>商品名称</td>
    	<td><input type="text" name="name" value="${itemsCustom.name}"/></td>
    </tr>
    <tr>
    	<td>商品价格</td>
    	<td><input type="text" name="price" value="${itemsCustom.price}"/></td>
    </tr>
    
    • 仅request的key等于pojo类型(首字母小写)时;
    • 相当于调用下边的代码model.addAttribute("itemsCustom", itemsCustom);
  2. 使用@ModelAttribute将方法的返回值传到页面;
    @RequestMapping("/editItemSubmit")
    public String editItemSubmit(Integer id,@ModelAttribute("item") ItemsCustom itemsCustom){}
    
    <tr>
    	<td>商品名称</td>
    	<td><input type="text" name="name" value="${item.name}"/></td>
    </tr>
    <tr>
    	<td>商品价格</td>
    	<td><input type="text" name="price" value="${item.price}"/></td>
    </tr>
    
  3. 最简单的方法是使用model,而不用@ModelAttribute
    @RequestMapping("/editItemSubmit")
    public String editItemSubmit(Model model,ItemsCustom itemsCustom){
    	// 可以直接使用model将提交pojo回显到页面
    	model.addAttribute("items", itemsCustom);
    }
    
    <tr>
    	<td>商品名称</td>
    	<td><input type="text" name="name" value="${item.name}"/></td>
    </tr>
    <tr>
    	<td>商品价格</td>
    	<td><input type="text" name="price" value="${item.price}"/></td>
    </tr>
    
    • 通过形参中的model将model数据传到页面,相当于modelAndView.addObject方法;

方法返回值

可以将方法返回值暴露为模型数据,传到视图页面。
如,将获取商品类型的方法的返回值,传递到页面:

// 商品分类
//itemtypes表示最终将方法返回值放在request中的key
@ModelAttribute("itemtypes")
public Map<String, String> getItemTypes() {

	Map<String, String> itemTypes = new HashMap<String, String>();
	itemTypes.put("101", "数码");
	itemTypes.put("102", "母婴");

	return itemTypes;
}
<select name="itemtype">
	<c:forEach items="${itemtypes}" var="itemtype">
		<option value="${itemtype.key}">${itemtype.value}</option>		
	</c:forEach>
</select>

异常处理器

springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。
处理思路:

  • 系统的dao、service、controller出现都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。

SpringMVC异常处理.png

自定义异常

为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。

public class CustomException extends Exception {

	/** serialVersionUID*/
	private static final long serialVersionUID = -5212079010855161498L;

	public CustomException(String message){
		super(message);
		this.message = message;
	}

	//异常信息
	private String message;

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}

自定义异常处理器

public class CustomExceptionResolver implements HandlerExceptionResolver {

	@Override
	public ModelAndView resolveException(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex) {

		ex.printStackTrace();

		CustomException customException = null;
		
		//如果抛出的是系统自定义异常则直接转换
		if(ex instanceof CustomException){
			customException = (CustomException)ex;
		}else{
			//如果抛出的不是系统自定义异常则重新构造一个未知错误异常。
			customException = new CustomException("未知错误,请与系统管理 员联系!");
		}
		
		ModelAndView modelAndView = new ModelAndView();
		modelAndView.addObject("message", customException.getMessage());
		modelAndView.setViewName("error");

		return modelAndView;
	}
}

错误页面

error.jsp:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt"  prefix="fmt"%> 
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<title>错误页面</title>
	</head>
	<body>
		您的操作出现错误如下<br/>
		${message }
	</body>
</html>

配置异常处理器

springmvc.xml中添加

<!-- 异常处理器 -->
<bean id="handlerExceptionResolver" class="cn.itcast.ssm.controller.exceptionResolver.CustomExceptionResolver"/>

异常测试

如:修改controller方法“editItem”,调用service查询商品信息,如果商品信息为空则抛出异常。

	// 调用service查询商品信息
	Items item = itemService.findItemById(id);
	
	if(item == null){
		throw new CustomException("商品信息不存在!");
	}

上传图片

json数据交互

RESTful支持

拦截器