框架 day69 SpringMVC高级(Validation校验,数据回显,上传,json数据交互,RESTful支持,拦截器)

第二天:高级知识

Validation校验方法(了解)

数据回显掌握

 

统一异常处理器掌握

上传图片(上传商品图片)掌握

json转换掌握

restFUL支持了解

拦截器掌握

 

1       注解开发-高级

 

 

 
1.1   Validation(了解)

 

         b/s系统中对http请求数据的校验多数在客户端进行,这也是出于简单及用户体验性上考虑,但是在一些安全性要求高的系统中服务端校验是不可缺少的,本节主要学习springmvc实现控制层添加校验。

         Spring3支持JSR-303验证框架,JSR-303 是JAVA EE 6 中的一项子规范,叫做BeanValidation,官方参考实现是Hibernate Validator(与Hibernate ORM 没有关系),JSR 303 用于对Java Bean 中的字段的值进行验证。

        

        

1.1.1        需求

         在商品信息修改提交时对商品信息内容进行校验,例如商品名称必须输入,价格合法性校验。

 

1.1.2        加入jar包

 框架 day69 SpringMVC高级(Validation校验,数据回显,上传,json数据交互,RESTful支持,拦截器)_第1张图片

 

1.1.3        配置validator

 



		
		
		
		
	

	
		
		   
       	     
            classpath:CustomValidationMessages 
       	    
    	
		
		
		
		
	


 

 

1.1.4        将validator加到处理器适配器

 

1.1.4.1     配置方式1

	 


1.1.4.2     配置方式2(自学)


	
		
	


 


	
		
	

1.1.5        添加验证规则

 

public class Items {
    private Integer id;
    @Size(min=1,max=30,message="{item.name.length.error}")
    private String name;
    
    @NotEmpty(message="{pic.is.null}")
    private String pic;

 

1.1.6        错误消息文件CustomValidationMessages


item.name.length.error=商品名称在1到30个字符之间
pic.is.null=请上传图片


 

如果在eclipse中编辑properties文件无法看到中文则参考“Eclipse开发环境配置-indigo.docx”添加propedit插件。

 

1.1.7        捕获错误

 

修改Controller方法:

// 商品修改提交
	@RequestMapping("/editItemSubmit")
	public String editItemSubmit(@Validated @ModelAttribute("item") Items items,BindingResult result,
			@RequestParam("pictureFile") MultipartFile[] pictureFile,Model model)
			throws Exception {
	//如果存在校验错误则转到商品修改页面
		if (result.hasErrors()) {
			List errors = result.getAllErrors();
			for(ObjectError objectError:errors){
				System.out.println(objectError.getCode());
				System.out.println(objectError.getDefaultMessage());
			}
			return "item/editItem";
		}


 

注意:添加@Validated表示在对items参数绑定时进行校验,校验信息写入BindingResult中,在要校验的pojo后边添加BingdingResult,一个BindingResult对应一个pojo,且BingdingResult放在pojo的后边。

 

商品修改页面显示错误信息:

页头:

<%@ 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" %>


在需要显示错误信息地方:



	${error.defaultMessage }


说明:

表示如果item参数绑定校验错误下边显示错误信息。

 

上边的方法也可以改为:

在controller方法中将error通过model放在request域,在页面上显示错误信息:

 

controller方法:

if(bindingResult.hasErrors()){
			model.addAttribute("errors", bindingResult);
}


页面:


	${error.defaultMessage }


 

 

 

 

1.1.8        分组校验

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

 

需求:商品修改提交只校验商品名称长度

 

定义分组:

分组就是一个标识,这里定义一个接口:

public interface ValidGroup1 {

}
public interface ValidGroup2 {

}

 

指定分组校验:

public class Items {
    private Integer id;
//这里指定分组ValidGroup1,此@Size校验只适用ValidGroup1校验
    @Size(min=1,max=30,message="{item.name.length.error}",groups={ValidGroup1.class})
    private String name;

// 商品修改提交
	@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.class}表示商品修改使用了ValidGroup1分组校验规则,也可以指定多个分组中间用逗号分隔,

@Validated(value={ValidGroup1.class,ValidGroup2.class})

 

1.1.9        校验注解


@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=)  被注释的元素必须在合适的范围内

 

1.2   数据回显


1.2.1        需求

表单提交失败需要再回到表单页面重新填写,原来提交的数据需要重新在页面上显示。

 

1.2.2        简单数据类型

对于简单数据类型,如:Integer、String、Float等使用Model将传入的参数再放到request域实现显示。

如下:

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


 

1.2.3        pojo类型

springmvc默认支持pojo数据回显,springmvc自动将形参中的pojo重新放回request域中,request的key为pojo的类名(首字母小写),如下:

	@RequestMapping("/editItemSubmit")
	public String editItemSubmit(Integer id,ItemsCustom itemsCustom)throws Exception{


springmvc自动将itemsCustom放回request,相当于调用下边的代码:

model.addAttribute("itemsCustom",itemsCustom);

 

jsp页面:

框架 day69 SpringMVC高级(Validation校验,数据回显,上传,json数据交互,RESTful支持,拦截器)_第2张图片 

页面中的从“itemsCustom”中取数据。

 

如果key不是pojo的类名(首字母小写),可以使用@ModelAttribute完成数据回显。


@ModelAttribute作用如下:


1、绑定请求参数到pojo并且暴露为模型数据传到视图页面

此方法可实现数据回显效果。

// 商品修改提交
	@RequestMapping("/editItemSubmit")
	public String editItemSubmit(Model model,@ModelAttribute("item") ItemsCustom itemsCustom)


页面:


	商品名称
	


	商品价格
	



 

如果不用@ModelAttribute也可以使用model.addAttribute("item", itemsCustom)完成数据回显。

 

 

2、将方法返回值暴露为模型数据传到视图页面

//商品分类
	@ModelAttribute("itemtypes")
	public Map getItemTypes(){
		
		Map itemTypes = new HashMap();
		itemTypes.put("101", "数码");
		itemTypes.put("102", "母婴");
		
		return itemTypes;
	}


页面:

商品类型:



 

1.3   异常处理器

 

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

        

1.3.1        异常处理思路

         系统中异常包括两类:预期异常和运行时异常RuntimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。

         系统的dao、service、controller出现都通过throwsException向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:

框架 day69 SpringMVC高级(Validation校验,数据回显,上传,json数据交互,RESTful支持,拦截器)_第3张图片 

 

 

1.3.2        自定义异常类

         为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果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;
	}
}


 

 

1.3.3        自定义异常处理器

 

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;
	}

}


 

1.3.4        错误页面

<%@ 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"%> 




错误页面



您的操作出现错误如下:
${message }


 

 

1.3.5        异常处理器配置

在springmvc.xml中添加:

 


	

 

1.3.6        异常测试

修改商品信息,id输入错误提示商品信息不存在。

 

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

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


在service中抛出异常方法同上。

 

1.3.7        扩展知识

1、异常处理器可以配置多个,如下配置两个异常处理器:

 

 

配置多个异常处理器的目的是针对不同的异常类型分别进行处理,并且springmvc在执行异常处理器时是按配置的顺序去执行的,但是需要注意的是除最后一个异常处理器返回ModelAndView外,其它的异常处理器返回null,springmvc源代码如下:

框架 day69 SpringMVC高级(Validation校验,数据回显,上传,json数据交互,RESTful支持,拦截器)_第4张图片 

 

 

 

1.4   上传图片

1.4.1        配置虚拟目录

在tomcat上配置图片虚拟目录,在tomcat下conf/server.xml中添加:

 

访问http://localhost:8080/pic即可访问F:\develop\upload\temp下的图片。

 

也可以通过eclipse配置:

 框架 day69 SpringMVC高级(Validation校验,数据回显,上传,json数据交互,RESTful支持,拦截器)_第5张图片

 

 

 

1.4.2        配置解析器


	
		
		
			5242880
		
	


 

1.4.3        jar包

CommonsMultipartResolver解析器依赖commons-fileupload和commons-io,加入如下jar包:


 

1.4.4        图片上传

    controller:

//商品修改提交
	@RequestMapping("/editItemSubmit")
	public String editItemSubmit(Items items, MultipartFile pictureFile)throws Exception{
		
		//原始文件名称
		String pictureFile_name =  pictureFile.getOriginalFilename();
		//新文件名称
		String newFileName = UUID.randomUUID().toString()+pictureFile_name.substring(pictureFile_name.lastIndexOf("."));
		
		//上传图片
		File uploadPic = new java.io.File("F:/develop/upload/temp/"+newFileName);
		
		if(!uploadPic.exists()){
			uploadPic.mkdirs();
		}
		//向磁盘写文件
		pictureFile.transferTo(uploadPic);

.....


    页面:

 


file的name与controller形参一致:

			
				商品图片
				
						
						


1.5   json数据交互

1.5.1        @RequestBody

作用:

@RequestBody注解用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容转换为json、xml等格式的数据并绑定到controller方法的参数上。

 

本例子应用:

@RequestBody注解实现接收http请求的json数据,将json数据转换为java对象

 

1.5.2        @ResponseBody

作用:

该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端

 

本例子应用:

@ResponseBody注解实现将controller方法返回对象转换为json响应给客户端

 

1.5.3        请求json,响应json实现:

1.5.3.1     环境准备

Springmvc默认用MappingJacksonHttpMessageConverter对json数据进行转换,需要加入jackson的包,如下:


1.5.3.2     配置json转换器

在注解适配器中加入messageConverters


	
		
		
		
		
		
	

 

注意:如果使用 则不用定义上边的内容。

 

 

1.5.3.3     controller编写

// 商品修改提交json信息,响应json信息
	@RequestMapping("/editItemSubmit_RequestJson")
	public @ResponseBody Items editItemSubmit_RequestJson(@RequestBody Items items) throws Exception {
		System.out.println(items);
		//itemService.saveItem(items);
		return items;

	}


 

1.5.3.4     页面js方法编写:

引入 js:

//请求json响应json
	function request_json(){
		$.ajax({
			type:"post",
			url:"${pageContext.request.contextPath }/item/editItemSubmit_RequestJson.action",
			contentType:"application/json;charset=utf-8",
			data:'{"name":"测试商品","price":99.9}',
			success:function(data){
				alert(data);
			}
		});
	}


 

1.5.3.5     测试结果:

 框架 day69 SpringMVC高级(Validation校验,数据回显,上传,json数据交互,RESTful支持,拦截器)_第6张图片

 

从上图可以看出请求的数据是json格式

 

 

1.5.4        请key/value,响应json实现:

表单默认请求application/x-www-form-urlencoded格式的数据即key/value,通常有post和get两种方法,响应json数据是为了方便客户端处理,实现如下:

 

1.5.4.1     环境准备

同第一个例子

 

1.5.4.2     controller编写

 

	// 商品修改提交,提交普通form表单数据,响应json
	@RequestMapping("/editItemSubmit_ResponseJson")
	public @ResponseBody Items editItemSubmit_ResponseJson(Items items) throws Exception {

		System.out.println(items);

//		itemService.saveItem(items);
		return items;
	}

 

1.5.4.3     页面js方法编写:

 

function formsubmit(){
	var user = " name=测试商品&price=99.9";
	alert(user);
	  $.ajax(
		{
			type:'post',//这里改为get也可以正常执行
			url:'${pageContext.request.contextPath}/item/ editItemSubmit_RequestJson.action',
//ContentType没指定将默认为:application/x-www-form-urlencoded
			data:user,
			success:function(data){
				alert(data.name);
			}
			
		}	
	)
}

 

从上边的js代码看出,已去掉ContentType的定义,ContentType默认为:application/x-www-form-urlencoded格式。

 

 

1.5.4.4     测试结果

 略

从上图可以看出请求的数据是标准的key/value格式。

 

 

1.5.5        小结

实际开发中常用第二种方法,请求key/value数据,响应json结果,方便客户端对结果进行解析。

 

 

1.6   RESTful支持


什么是RESTful

 

rest即RepresentationalState Transfer,表现层状态转化

 

互联网上资源(是服务)细化理解为一个url,如果访问某个资源通过http url访问。

 

我们把"资源"具体呈现出来的形式,叫做它的"表现层"(Representation)

 

表现层对用户展示的形式:html、json、xml、pdf、图片。。

 

如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。

 

GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。

 

目前常用就是get、post。

 

 

RESTful就是对http最全面最好的诠释,RESTful就是进行互联网开发的理念、思想。

 

RESTful开发:

1、url规范

原始url:http://www....../queryItems.action?type=101&id=111...

规范后:http://wwww.../item/101/1

比如:

对商品查询

url:http://wwww.../item/101/1表示对101分类下的1号商品查询

http请求方法设置为get

比如:

对商品删除

url:http://wwww.../item/101/1表示对101分类 下的1号商品删除

http请求方法设置为delete

 

2、给用户展示信息

在请求时指定服务端给客户端响应的内容类型是什么?

实现:在请求时设置http头中Accept

对商品查询,以json格式展示:

rest设置:

url为:http://wwww.../item/101/1

客户端请求此url并且设置Accept为"applicatoin/json"

 

服务端处理方法:

接收请求,解析Accept,根据指定类型返回不同的内容格式。

如果解析到Accept为"applicatoin/json",服务端就将内容转成json输出

 

如果解析到Accept为"applicatoin/xml",服务端就将内容转成xml输出

 

 

 

RESTful架构的特点:

综合上面的解释,我们总结一下什么是RESTful架构:

  (1)每一个URI代表一种资源;

实现:

规范后的url规范后:http://wwww.../item/101/1,特点是参数通过url传递。

  (2)客户端和服务器之间,传递这种资源的某种表现层;

实现:

服务端解析Accept,根据客户端的要求将内容进行输出。

 

  (3)客户端通过四个HTTP动词,对服务器端资源进行操作,实现"表现层状态转化"。

实现:

服务解析客户端请求的http方法,根据不同的方法执行不同的service操作。

 

 

 

当前的现状:

 

实现第一点:url实现rest风格。

实现第二点:对外提供rest接口,输出json数据。

一般没有实现第三种的。



1.6.1        需求

RESTful方式实现商品信息查询,返回json数据

 

1.6.2        添加DispatcherServlet的rest配置

 

	
		springmvc-servlet-rest
		org.springframework.web.servlet.DispatcherServlet
		
			contextConfigLocation
			classpath:spring/springmvc.xml
		
	
	
		springmvc-servlet-rest
		/
	

1.6.3        URL模板模式映射

@RequestMapping(value="/ viewItems/{id}"):{×××}占位符,请求的URL可以是“/viewItems/1”或“/viewItems/2”,通过在方法中使用@PathVariable获取{×××}中的×××变量。

@PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上。

 

@RequestMapping("/viewItems/{id}") 
	public @ResponseBody viewItems(@PathVariable("id") String id,Model model) throws Exception{
		//方法中使用@PathVariable获取useried的值,使用model传回页面
		//调用 service查询商品信息
		ItemsCustom itemsCustom = itemsService.findItemsById(id);
		return itemsCustom;
}

 

如果RequestMapping中表示为"/viewItems/{id}",id和形参名称一致,@PathVariable不用指定名称。

 

 

1.6.4        静态资源访问

如果在DispatcherServlet中设置url-pattern为 /则必须对静态资源进行访问处理。

spring mvc 的实现对静态资源进行映射访问。

如下是对js文件访问配置:

 

 

2       拦截器

2.1   定义

         SpringWeb MVC 的处理器拦截器类似于Servlet 开发中的过滤器Filter,用于对处理器进行预处理和后处理。

 

2.2   拦截器定义

实现HandlerInterceptor接口,如下:

Public class HandlerInterceptor1 implements HandlerInterceptor{

	/**
	 * controller执行前调用此方法
	 * 返回true表示继续执行,返回false中止执行
	 * 这里可以加入登录校验、权限拦截等
	 */
	@Override
	Public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		// TODO Auto-generated method stub
		Return false;
	}
	/**
	 * controller执行后但未返回视图前调用此方法
	 * 这里可在返回用户前对模型数据进行加工处理,比如这里加入公用信息以便页面显示
	 */
	@Override
	Public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		// TODO Auto-generated method stub
		
	}
	/**
	 * controller执行后且视图返回后调用此方法
	 * 这里可得到执行controller时的异常信息
	 * 这里可记录操作日志,资源清理等
	 */
	@Override
	Public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// TODO Auto-generated method stub
		
	}

}



2.3   拦截器配置

2.3.1        针对某种mapping配置拦截器


	
		
			
			
		
	

	
	


 

 

2.3.2        针对所有mapping配置全局拦截器



	
	
		
		
	
	
		
		
	



 

2.4   正常流程测试

 

2.4.1        代码:

定义两个拦截器分别为:HandlerInterceptor1和HandlerInteptor2,每个拦截器的preHandler方法都返回true。

框架 day69 SpringMVC高级(Validation校验,数据回显,上传,json数据交互,RESTful支持,拦截器)_第7张图片 

 

 

2.4.2        运行流程

HandlerInterceptor1..preHandle..

HandlerInterceptor2..preHandle..

 

HandlerInterceptor2..postHandle..

HandlerInterceptor1..postHandle..

 

HandlerInterceptor2..afterCompletion..

HandlerInterceptor1..afterCompletion..

 

 

2.5   中断流程测试

2.5.1        代码:

定义两个拦截器分别为:HandlerInterceptor1和HandlerInteptor2。

 

 

2.5.2        运行流程

HandlerInterceptor1的preHandler方法返回false,HandlerInterceptor2返回true,运行流程如下:

 

HandlerInterceptor1..preHandle..

 

从日志看出第一个拦截器的preHandler方法返回false后第一个拦截器只执行了preHandler方法,其它两个方法没有执行,第二个拦截器的所有方法不执行,且controller也不执行了。

 

 

HandlerInterceptor1的preHandler方法返回true,HandlerInterceptor2返回false,运行流程如下:

 

HandlerInterceptor1..preHandle..

HandlerInterceptor2..preHandle..

HandlerInterceptor1..afterCompletion..

 

从日志看出第二个拦截器的preHandler方法返回false后第一个拦截器的postHandler没有执行,第二个拦截器的postHandler和afterCompletion没有执行,且controller也不执行了。

 

总结:

preHandle按拦截器定义顺序调用

postHandler按拦截器定义逆序调用

afterCompletion按拦截器定义逆序调用

 

postHandler在拦截器链内所有拦截器返成功调用

afterCompletion只有preHandle返回true才调用

 

 

2.6   拦截器应用

 

2.6.1        用户身份认证

 

public class LoginInterceptor implements HandlerInterceptor{

	@Override
	Public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {

		//如果是登录页面则放行
		if(request.getRequestURI().indexOf("login.action")>=0){
			return true;
		}
		HttpSession session = request.getSession();
		//如果用户已登录也放行
		if(session.getAttribute("user")!=null){
			return true;
		}
		//用户没有登录挑战到登录页面
		request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
		
		return false;
	}}


 

2.6.2        用户登陆controller

	//登陆提交
	//userid:用户账号,pwd:密码
	@RequestMapping("/login")
	public String loginsubmit(HttpSession session,String userid,String pwd)throws Exception{
		
		//向session记录用户身份信息
		session.setAttribute("activeUser", userid);
		
		return "redirect:item/queryItem.action";
	}
	
	//退出
	@RequestMapping("/logout")
	public String logout(HttpSession session)throws Exception{
		
		//session过期
		session.invalidate();
		
		return "redirect:item/queryItem.action";
	}


 


 

你可能感兴趣的:(笔记)