Springboot学习笔记(八)——SpringMVC(二)

处理器映射

web工程使用了SpringMVC,那么他在启动阶段就会将注解@RequestMapping所配置的内容保存到处理器映射(HanlderMapping)机制中去,然后等待请求的到来,通过拦截器请求的信息与handlerMapping进行匹配,找到对应的处理器(他包含处理器逻辑),并将处理器及其拦截器保存到HandlerExecutionChain对象中,返回给DispatcherServlet,这样DispatcherServlet就可以运行他们了。从以上论述中可以看出,HandlerMapping的主要任务是将请求定位到具体的处理器上。
RequestMapping源代码:

package org.springframework.web.bind.annotation;
/**import**/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {
	//配置请求映射名称
	String name() default "";
	//通过路径映射
	@AliasFor("path")
	String[] value() default {};
	//通过路径映射会回path配置项
	@AliasFor("value")
	String[] path() default {};
	//限定只响应HTTP请求类型,如GET、POST、HEAD、OPTIONS、PUT、TRACE等
	//在默认情况下可以响应所有请求类型
	RequestMethod[] method() default {};
	//当存在对应的HTTP请求参数时才响应
	String[] params() default {};
	//限定请求头存在对应的参数时才响应
	String[] headers() default {};
	//限定HTTP请求提交类型,如“application/json”,“text/html”
	String[] consumes() default {};
	//限定返回的内容类型,仅当HTTP请求头中的accept类型中包含该指定类型时才返回
	String[] produces() default {};
}

路径是必须的配置项,这里的method配置项可以限定http的请求类型,这是最常用的配置项,可以区分http的get或post等不同的请求。为了简化method配置项的配置新增了几个注解,如@GetMapping,@PostMapping,@PatchMapping,@PutMapping和@DeleteMapping。从名称可以看出@GetMapping对应的是HTTP的GET方法,@PostMapping对应的是HTTP的POST方法。

获取控制器参数

处理器是对控制器的包装,在处理器运行的过程中会调度控制器的方法,只是他在进入控制器方法之前会对http的参数和上下文进行解析,将他们转换为控制器所需的参数。这一步是处理器首先需要做的事情,只是在大部分情况下不需要自己去开发这一步,因为springMVC已经提供了大量的转换规则,通过这些规则就能非常简易的获取大部分的参数。

  1. 在无注解下获取参数
    在没有注解的情况下,SpringMVC也可以获取参数,且参数允许为空,唯一的要求是参数名称和HTTP请求的参数名称保持一致,如下代码所示:
package com.springboot.chapter10.controller;
/**import**/
@Controller
@RequestMapping("/my")
public class MyController {
	/**
	 * 在无注解的情况下获取参数,要求参数名称和http请求参数名称一致
	 * @param intVal
	 * @param longVal
	 * @param str
	 * @return  响应json参数
	 */
	//http get请求
	@GetMapping("/no/annotation")
	@ResponseBody
	public Map<String,Object> noAnnotation(Integer intVal,Long longVal,String str){
		Map<String,Object> map = new HashMap<String, Object>();
		map.put("intVal", intVal);
		map.put("longVal", longVal);
		map.put("str", str);
		return map; 
	}
}

启动springboot应用后,在浏览器中请求URL:

http://localhost:8806/my/no/annotation?intVal=10&longVal=200

从代码中可以看出控制器方法参数中还有一个字符串参数str,但因为参数在默认的规则下可以为空,所以这个请求并不会报错,因为方法标注了@ResponseBody,所以在控制器返回的结果就会转化为JSON数据集。

  1. 使用@RequestParams获取参数
    在无需任何注解的情况下,就要求HTTP参数和控制器方法参数名称保持一致。然而在前后端分离的趋势下,前端的命名规则可能与后端的规则不同,这时需要把前端的参数与后端的对应起来。SpringMVC提供了注解@RequestParam来确定前后端参数名称的映射关系,如下代码:
/**
	 * 通过注解@RequestParam获取参数,用于前后端参数不一致
	 * @RequestParam(value = "str_val",required = false)String str  
	 *  加一个required = false允许参数为空
	 * @param intVal
	 * @param longVal
	 * @param str
	 * @return
	 */
	@GetMapping("/param")
	@ResponseBody
	public Map<String, Object> requestParam(
			@RequestParam("int_val")Integer intVal,
			@RequestParam("long_val")Long longVal,
			@RequestParam(value = "str_val",required = false)String str){
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("intVal", intVal);
		map.put("longVal", longVal);
		map.put("str", str);
		return map;
	}

从代码中可以看出,在方法参数处使用了注解@RequestParam,其目的是指定HTTP参数和方法参数的映射关系,这样处理器就会按照其配置的映射关系来得到参数,然后调用控制器的方法。启动SpringBoot应用后,在浏览器地址栏输入http://localhost:8080/my/param?int_val=1&long_val=2&str_val=str,就能够看到请求的结果了。但如果把3个HTTP参数中的任意一个删去,就会得到异常报错的信息,因为在默认的情况下@RequestParam标注的参数是不能为空的,为了让他能够为空,可以配置其属性required为false,例如:

@RequestParam(value = "str_val",required = false)String str

这样,对应的参数就可以为空了。

  1. 传递数组
    在springMVC中,除了可以像上面那样传递一些简单的值外,还可以传递数组。springMVC内部已经能够支持用逗号分隔的数组参数。
/**
	 * 传递数组参数
	 * @param intArr
	 * @param longArr
	 * @param strArr
	 * @return
	 */
	@RequestMapping("/array")
	@ResponseBody
	public Map<String, Object> requestArray(
			@RequestParam("int_arr")Integer[] intArr,
			@RequestParam("long_arr")Long[] longArr,
			@RequestParam("str_arr")String[] strArr){
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("intArr", intArr);
		map.put("longArr", longArr);
		map.put("strArr", strArr);
				return map;
	}

方法里定义了采用数组,那么前端就需要依照一定的规则传递给这个方法,例如,输入http://localhost:8080/my/array?int_arr=1,2,3&long_arr=4,5,6&str_arr=str1,str2,str3,可以看到需要传递数组参数时,每个参数的数组元素只需要通过逗号分隔即可。

  1. 传递JSON
    在前后端分离的情况下,使用json已经十分普遍了。对于前端的页面或者手机应用,可以通过请求后端的json数据集,这样它们就能方便地将数据渲染到视图中。有时前端也需要提交复杂的数据到后端,为了更好的组织和提高代码的可读性,可以将数据转换为json数据集,通过HTTP请求体提交给后端,对此springMVC也提供了良好的支持。
    新增用户表单:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>新增用户</title>
<script src="https://code.jquery.com/jquery-3.2.0.js"></script>
<script type="text/javascript">
	$(document).ready(function(){
		$("#submit").click(function(){
			var user_name = $("#user_name").val();
			var note = $("#note").val();
			if($.trim(user_name)==''){
				alert("用户名不能为空!");
				return;
			}
			var params = {
					user_name : user_name,
					note : note
			};
			$.post({
				url : "./insert",
				//此处告知传递参数类型为json,不能缺少
				contentType : "application/json",
				//将json转化为字符串
				data : JSON.stringify(params),
				//成功后的方法,方法回调
				success : function(result){
					if(result == null|| result.id == null){
						alert("插入失败!");
						return;
					}
					alert("插入成功!");
				}
			});
		});
	});
</script>
</head>
<body>
<div style="margin: 20px 0;" align="center">
	<form id="insertForm">
		<table>
			<tr>
				<td>用户名称:</td>
				<td><input id="user_name" name="user_name"></td>
			</tr>
			<tr>
				<td>备注:</td>
				<td><input id="note" name="note"></td>
			</tr>
			<tr>
				<td></td>
				<td align="center"><input id="submit" type="button" value="提交" ></td>
			</tr>
		</table>
	</form>
</div>
</body>
</html>

使用jQuery进行ajax提交。这里先组织了一个json数据集,而且把提交类型也设置为了json类型,然后才提交到控制器。这样控制器就可以得到一个json数据集的请求体了。

package com.springboot.chapter10.controller;
@RequestMapping("/user")
@Controller
public class UserController {
	@Autowired
	private UserService userService;

	/**
	 * 打开请求页面
	 * @return
	 */
	@RequestMapping("/add")
	public String add() {
		return "/user/add";
	}
	/**
	 * 新增用户,@RequestBody注解得到页面传过来的json参数
	 * @param user
	 * @return
	 */
	@PostMapping("/insert")
	@ResponseBody
	public User insert(@RequestBody User user) {
		userService.insert(user);
		return user;
	}
}

在这里插入图片描述 从图中可以看到请求的add方法就能够打开jsp页面,入录表单后,启动浏览器的监控功能,然后按下提交按钮,在监控区域可以看到后端返回了新增用户信息,并以json数据集给予展示。

  1. 通过URL传递参数
    在一些网站中,提出了REST风格,这时参数往往通过URL进行传递。例如获取编号为1的用户,URL就要写成/user/1,这里1代表的是用户编号(id)。SpringMVC对此也提供了良好的支持,可以通过处理器映射和注解@PathVariable的组合获取URL参数。首先通过处理器可以定位参数的位置和名称,而@PathVariable则可以通过名称来获取参数。
/**
	 * 通过URL传递参数 查询单个用户,Rest风格,处理器先定位参数,@PathVariable注解来获取参数
	 * @param id
	 * @return
	 */
	// {...}代表占位符,还可以配置参数名称
	@GetMapping("/{id}")
	@ResponseBody
	public User getUser(@PathVariable("id") Integer id) {
		User user = userService.getUser(id);
		return user;
	}

代码中首先通过@GetMapping指定一个URL,然后用{…}来标明参数的位置和名称。这里指定名称为id,这样springMVC就会根据请求去匹配这个方法。@PathVariable配置的字符串为id,它对应URL的参数声明,这样spring就知道如何从URL中获取参数。

  1. 获取格式化参数
    在一些应用中,往往需要格式化数据,其中最为典型的当属时间和货币。例如,在一些系统中日期格式约定为yyyy-MM-dd,金额约定为货币符号和用逗号分隔。同样地,springmvc中也对此提供了良好的支持。对日期和数字类型的转换注解进行处理,分别是@DateTimeFormat和@NumberFormat。其中@DateTimeFormat是针对日期进行格式化的,@NumberFormat则是针对数字进行格式化的。
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>格式转换</title>
</head>
<body>
<form action="./commit" method="post">
		<table>
			<tr>
				<td>日期(yyyy-MM-dd)</td>
				<td><input type="text" name="date" value="2017-08-08" /></td>
			</tr>
			<tr>
				<td>金额(#,###.##)</td>
				<td><input type="text" name="number" value="1,234,567.89" /></td>
			</tr>
			<tr>
				<td colspan="2" align="right"><input type="submit" value="提交" />
				</td>
			</tr>
		</table>
	</form>
</body>
</html>
/***
	 * 打开formatter页面
	 * @return
	 */
	@GetMapping("/format/form")
	public String showFormat(){
		return "/format/formatter";
	}
	/**
	 * 日期和数字格式化转换
	 * @param date
	 * @param number
	 * @return
	 */
	@PostMapping("/format/commit")
	@ResponseBody
	public Map<String, Object> format(@DateTimeFormat(iso = ISO.DATE)Date date,
			@NumberFormat(pattern = "#,###.##")Double number){
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("date", date);
		map.put("number", number);
		return map;
	}

在springboot中,日期参数的的格式化也可以不使用@DateFormat,而只是在配置文件application.properties中加入如下配置项即可:

spring.mvc.date-formate=yyyy-MM-dd

在这里插入图片描述
在这里插入图片描述

自定义参数转换规则

当与第三方公司进行合作,参数从第三方公司以密文的形式传递,或者其所定义的参数规则是现有springMVC所不能的支持的,这是则需要通过自定义参数转换规则来满足这些特殊的要求。
在springMVC中只需要简单的注解,甚至是不用任何注解就能够得到参数。那是因为springMVC提供的处理器会先以一套规则来实现参数的转换。实际上处理器的转换规则还包含控制器返回后的处理。
HTTP的请求包含请求头(header)、请求体(body)、URL和参数等内容,服务器还包含其上下文环境和客户端交互会话(Session)机制,而这里的消息转换是指请求体的转换。下面是springMVC是如何从HTTP请求中获取参数的。

  1. 处理器获取参数逻辑
    当请求来到时,在处理器执行的过程中,首先会从HTTP请求和上下文环境来得到参数。如果是简易的额参数他会以简易的转换器进行转换,而这些简单的转换器时springMVC自身已经提供了的。但是如果是转换HTTP请求体(body),他就会调用HTTPmassageConverter接口的方法对请求体的信息进行转换,首先他会判断能否对请求体进行转换,如果可以将会转换成java类型。
package org.springframework.http.converter;
/**import**/
public interface HttpMessageConverter<T> {
	//是否可读,其中clazz为java类型,mediaType为HTTP请求类型
	boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
	//判断clazz类型是否能够转换为mediaType媒体类型
	//其中calzz为java类型,mediaType为HTTP响应类型
	boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
	//可支持的媒体类型列表
	List<MediaType> getSupportedMediaTypes();
	//当canRead验证通过后,读入HTTP请求信息
	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;
	//当canWrite方法验证通过后,写入响应
	void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;
}

在UserController类中,控制器的方法参数标注了@RequestBody,所以处理器会采用请求体(body)的内容进行参数转换,而前端的请求体为json类型,所以首先他会调用canRead方法来确定请求体是否可读。如果判定可读后,接着就是使用read方法,将前端提交的用户json类型的请求体转换为控制器的用户(User)类参数,这样控制器就得到参数了。
HttpMessageConverter接口只是将HTTP的请求体转换为对应的java对象,而对于HTTP参数和其他内容,以性别参数来说,前端可能传递给控制器的是一个整数,而控制器参数却是一个枚举,这就需要提供自定义的参数转换规则了。
自定义参数转换规则,首先需要了解处理器转换参数的过程。在springMVC中,是通过webDataBinder机制来获取参数的,他的主要作用是解析HTTP请求的上下文,然后在控制器调用之前转换参数并且提供验证的功能,为调用控制器的方法做准备。处理器会从HTTP请求中读取数据,然后通过三种接口来进行各类参数转换,这三种接口是Converter、Formatter和GenericConverter。在springmvc的机制中,这三种记得实现类都采用了注册机的机制,默认的情况下springMVC已经在注册机内注册了许多的转换器,这样就可以实现大部分数据类型的转换,所以在大部分情况下无需开发者再提供转换器。同样地,在自定义转换规则时,只需要在注册机上注册自己的转换器就可以了。
实际上,webDataBinder机制还有一个重要的功能,就是验证转换结果。
控制器的参数是处理器通过Converter、Formatter和GenericConverter这三个接口转换出来的。首先,Converter是一个普通的转换器,例如,有一个Integer类型的控制器参数,而从HTTP对应的为字符串,对应的Converter就会将字符串转换为Integer类型;其次,Formatter是一个格式化转换器,类似日期字符串就是通过他按照约定的格式转换为日期的;最后,GenericConverter转换器则将HTTP参数转换为数组。
对于数据类型转换,springMVC提供了一个服务机制去管理,就是ConversionService接口。在默认情况下,会使用这个接口的子类DefaultFormattingConversionService对象来管理这些转换类。

  1. 一对一转换器(Converter)
    Converter是一对一的转化器,也就是从一种类型转换为另一种类型。
    Converter接口源码:
package org.springframework.core.convert.converter;

import org.springframework.lang.Nullable;

@FunctionalInterface
public interface Converter<S, T> {
	@Nullable
	//转换方法,S代表原类型,T代表目标类型
	T convert(S source);
}

测试字符串用户转换器:

package com.springboot.chapter10.converter;
/**import**/
/**
 * 自定义字符串用户转换器
 * @author Administrator
 */
@Component
public class StringToUserConverter implements Converter<String, User> {
	@Override
	public User convert(String source) {
		// TODO Auto-generated method 
		User user = new User();
		String [] str = source.split("-");
		user.setId(Integer.parseInt(str[0]));
		user.setUser_name(str[1]);
		user.setNote(str[2]);
		return user;
	}
}

在这里插入图片描述

  1. GenericConverter集合和数组转换
    GenericConverter是数组转换器,因为springMVC自身就提供了一些数组转换器,需要自定义的并不多。假设需要同时新增多个用户,这样便需要传递一个用户列表(List< User>)给控制器。此时springmvc会使用StringToCollectionConverter转换他,这个类就实现了GenericConverter接口,并且是SpringMVC内部已经注册的数组转换器。他首先会把字符串用逗号分隔为一个个的子字符串,然后根据原类型泛型为String、目标类型为User类,找到对应的Converter进行转换,将子字符串转换为User对象。
/**
	 * 使用集合传递多个用户,传递过来的请求参数先用StringToCollectionConverter处理,
	 * 再用自定义的StringToUserConverter转换成user
	 * @param list
	 * @return
	 */
	@GetMapping("/list")
	@ResponseBody
	public List<User> list(List<User> userList) {
		return userList;
	}

这里的参数使用了一个个逗号分隔,StringToCollectionConverter在处理时也就通过逗号分隔,然后通过之前自定义的转换器StringToUserConverter将其变为用户类对象,再组成一个列表(List)转递给控制器。
在这里插入图片描述

数据验证

在处理器逻辑中谈到了参数的转换,转换参数出来之后,紧接着就是验证参数的合法性,因此SpringMVC也就提供了验证参数的机制。一方面,他可以支持JSR-303注解验证,在默认的情况下,springboot会引入关于Hibernate Validator机制来支持JSR-303验证规范;另外一方面,因为业务比较复杂,所以需要自定义验证规则。

  1. JSR-303验证
    JSR-303验证主要是通过注解的方式进行的。这里先定义一个需要验证的pojo,此时需要在其属性中加入相关的注解。
package com.springboot.chapter10.pojo;
/**import**/
/**
 * 验证JSR-303
 * @author Administrator
 */
public class ValidatorPojo {
	//非空判断
	@NotNull(message = "id不能为空")
	private Integer id;
	
	@Future(message = "需要一个将来日期")//只能是将来的日期
	//@Past	//只能是过去的日期
	@DateTimeFormat(pattern = "yyyy-MM-dd")//日期格式转换
	@NotNull	//非空判断
	private Date date;
	
	@NotNull
	@DecimalMin(value = "0.1") //最小值为0.1元
	@DecimalMax(value = "10000.00") //最大值为10000元
	private Double doubleValue;
	
	@Max(value = 88,message = "最大值为88")	//最大值为88
	@Min(value = 1,message = "最小值为1")		//最小值为1
	@NotNull
	private Integer integer;
	
	@Range(min = 1,max = 888,message = "范围是1到888")		//限定范围
	private Long range;
	
	@Email(message = "邮箱格式错误")	//邮箱验证
	private String mail;
	
	@Size(min = 20,max = 30,message = "字符串长度要求在20到30之间")
	private String size;
	//get和set方法
}
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试JSR-303</title>
<!-- 加载Query文件-->
<script src="https://code.jquery.com/jquery-3.2.0.js"></script>
<script type="text/javascript">
	$(document).ready(function(){
		//请求验证的pojo
		var pojo = {
				id:null,
				date:'2019-09-29',
				doubleValue:999999.09,
				integer:100,
				range:1000,
				email:'email',
				size:'adv1212',
				regexp:'a,b,c,d'
		}
		$.post({
			url:"./validate",
			contentType:"application/json",
			data:JSON.stringify(pojo),
			success:function(result){
			}
		});
	});
</script>
</head>
<body></body>
</html>

打开页面和后台验证方法

@GetMapping("/valid/page")
	public String validPage() {
		return "/validator/pojo";
	}
	/**
	 * 解析验证参数错误
	 * @param vp   需要验证的pojo,使用注解@Valid表示验证
	 * @param errors	错误信息,他由springMVC通过验证pojo后自动填充
	 * @return		错误信息map
	 */
	@RequestMapping("/valid/validate")
	@ResponseBody
	public Map<String, Object> validate(@Valid @RequestBody ValidatorPojo vp,Errors errors){
		Map<String, Object> errMap = new HashMap<String, Object>();
		//获取错误列表
		List<ObjectError> oes = errors.getAllErrors();
		for (ObjectError oe : oes) {
			String key = null;
			String msg = null;
			//字段错误
			if(oe instanceof FieldError) {
				FieldError fe = (FieldError) oe;
				key = fe.getField();	//获取错误验证字段名
			}else {
				//非字段错误
				key = oe.getObjectName();		//获取验证对象名
			}
			msg = oe.getDefaultMessage();
			errMap.put(key, msg);
		}
		return errMap;
	}

@RequestBody代表着接收一个json参数,这样spring就会获取页面通过ajax提交的json请求体,然后@Valid注解则表示启动验证机制,这样spring就会启用JSR-303验证机制进行验证。他会自动将最后的验证结果放入Errors对象中,这样就可以获取相关验证过后的信息。
在这里插入图片描述

  1. 参数验证机制
    为了更加灵活的提供验证机制,Spring只提供自己的验证机制。在参数转换时,可以看到在springMVC中,存在WebDataBinder机制进行管理,在默认情况下Spring会自动地根据上下文通过注册了的转换器转换出控制器所需的参数。在WebDataBinder中除了可以注册转换器外,还允许注册验证器(Validator)。
    在spring控制器中,它还允许使用注解@InitBinder,这个注解的作用是允许在进入控制器方法前修改WebDataBinder机制。
    springMVC的验证机制接口Validator:
package org.springframework.validation;
public interface Validator {
	/**
	*判定当前验证器是否支持该Class类型的验证
	*@param clazz  pojo类型
	*@return  当前验证器是否支持该pojo类型
	*/
	boolean supports(Class<?> clazz);
	/*
	*如果supports返回true,则这个方法执行验证逻辑
	*@param  target被验证pojo对象
	*@param  errors错误对象
	*/
	void validate(Object target, Errors errors);
}

实例:
自定义用户验证器

package com.springboot.chapter10.validator;
/**import**/
/**
 * 自定义用户验证器
 * @author Administrator
 */
public class UserValidator implements Validator {
	//该类只支持User类验证
	@Override
	public boolean supports(Class<?> clazz) {
		// TODO Auto-generated method stub
		return clazz.equals(User.class);
	}
	//验证逻辑
	@Override
	public void validate(Object target, Errors errors) {
		// TODO Auto-generated method stub
		//对象为空
		if(target == null) {
			errors.rejectValue("", null, "用户不能为空");
		}
		//强制转换
		User user = (User) target;
		//用户名非空串
		if(StringUtils.isEmpty(user.getUser_name())) {
			//增加错误,可以进入控制器方法
			errors.rejectValue("user_name", null, "用户名不能为空");
		}
	}
}

有了这个验证器,spring还不会启动它,因为还没有绑定给webDataBinder机制。在springMVC提供了一个注解@InitBinder,它的作用是在执行控制器方法之前,处理器会先执行被@InitBinder标注的方法。这时可以将webDataBinder对象作为参数传递到方法中,通过这层关系得到webDataBinder对象,这个对象有一个setValidator方法,他可以绑定自定义的验证器,他可以绑定自定义的验证器,这样就可以在获取参数之后,通过自定义的验证器去验证参数。
绑定验证器:

/**
	 * 调用控制器前先执行这个方法
	 * 
	 * @param binder
	 */
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		// 绑定验证器
		binder.setValidator(new UserValidator());
		// 定义日期参数格式,参数不在需要注解@DataTimeFormat,boolean参数表示是否允许为空
		binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false));
	}

	/**
	 * 
	 * @param user   用户对象用StringToUserConverter转换
	 * @param errors 验证器返回的错误
	 * @param date   因为WebDataBinder已经绑定了格式,所以不再需要注解
	 * @return
	 */
	@GetMapping("/validator")
	@ResponseBody
	public Map<String, Object> validator(@Valid User user, Errors errors, Date date) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("user", user);
		map.put("date", date);
		if (errors.hasErrors()) {
			// 获取全部错误
			List<ObjectError> oes = errors.getAllErrors();
			for (ObjectError oe : oes) {
				// 判断是否字段错误
				if (oes instanceof FieldError) {
					// 字段错误
					FieldError fe = (FieldError) oe;
					map.put(fe.getField(), fe.getDefaultMessage());
				} else {
					// 对象错误
					map.put(oe.getObjectName(), oe.getDefaultMessage());
				}
			}
		}
		return map;
	}

在这里插入图片描述

数据模型

在springMVC的应用中,控制器是业务逻辑核心内容,而控制器的核心内容之一就是对数据的处理。通过对springMVC全流程的学习,可以看到允许控制器自定义模型和视图(ModelAndView),其中模型是存放数据的地方,视图则是展示给用户。
数据模型的作用是绑定数据,为后面的视图渲染做准备
SpringMVC数据模型设计图
在这里插入图片描述从上图中可以看出,在类ModelAndView中存在一个ModelMap类型的属性,ModelMap继承了LinkHashMap类,所以他具备map接口的一切特性,除此之外它还可以增加数据属性。在springMVC应用中,如果在控制器方法的参数中使用ModelAndView、Model或者ModelMap作为参数类型,SpringMVC会自动创建数据模型对象,如下代码:

package com.springboot.chapter10.controller;
/**imports**/
@Controller
@RequestMapping("/data")
public class DataModelController {
	@Autowired
	private UserService userService;

	// 测试Model接口
	@GetMapping("/model")
	public String useModel(Integer id, Model model) {
		User user = userService.getUser(id);
		model.addAttribute("user", user);
		return "/data/user";
	}

	// 测试modelmap类
	@GetMapping("/modelMap")
	public ModelAndView useModelMap(Integer id, ModelMap modelMap) {
		User user = userService.getUser(id);
		ModelAndView mav = new ModelAndView();
		// 设置视图名称
		mav.setViewName("/data/user");
		// 设置数据模型,此处modelmap没有与modelandview绑定,这步系统会自动处理
		modelMap.addAttribute("user", user);
		return mav;
	}
	
	// 测试modelAndview
	@GetMapping("/mav")
	public ModelAndView useModelAndView(Integer id, ModelAndView mv) {
		User user = userService.getUser(id);
		mv.setViewName("/data/user");
		mv.addObject("user", user);
		return mv;
	}
}

用户视图如下:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>用户信息</title>
</head>
<body>
	<table>
        <tr>
            <td>编号</td>
            <td>${user.id}</td>
        </tr>
        <tr>
            <td>用户名</td>
            <td>${user.user_name}</td>
        </tr>
        <tr>
            <td>备注</td>
            <td>${user.note}</td>
        </tr>
    </table>
</body>
</html>

视图和视图解析器

视图是渲染数据模型展示给用户的组件,在springMVC中又分为逻辑视图和非逻辑视图。逻辑视图是需要视图解析器(ViewResolver)进行进一步定位。对于非逻辑视图,则并不需要进一步地定位视图的位置,他只需要直接将数据模型渲染出来即可。

  1. 视图实例——导出PDF文件
    AbstractPdfView属于非逻辑视图,所以它并不需要任何的视图解析器(ViewResolver)去定位。
    在Maven的配置文件中加入相关的依赖:
		<dependency>
			<groupId>org.xhtmlrenderer</groupId>
			<artifactId>core-renderer</artifactId>
			<version>R8</version>
		</dependency>
		<dependency>
			<groupId>com.itextpdf</groupId>
			<artifactId>itextpdf</artifactId>
			<version>5.5.12</version>
		</dependency>

定义PDF导出接口:

package com.springboot.chapter10.view;
/**imports**/
public interface PdfExportService {
	public void make(Map<String, Object> model, Document document, PdfWriter writer,
			HttpServletRequest request, HttpServletResponse response);
}

PDF导出视图类

package com.springboot.chapter10.view;
/**imports**/
public class PdfView extends AbstractPdfView {
	//导出服务接口
	private PdfExportService pdfExportService;
	
	//创建对象是载入导出服务接口
	public PdfView(PdfExportService pdfExportService) {
		super();
		this.pdfExportService = pdfExportService;
	}
	//调用接口实现
	@Override
	protected void buildPdfDocument(Map<String, Object> model, Document document, PdfWriter writer,
			HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		pdfExportService.make(model, document, writer, request, response);
	}
}

在用户控制器中导出PDF数据

// 导出接口
	@GetMapping("/export/pdf")
	public ModelAndView exportPdf() {
		// 查询用户信息列表
		List<User> userList = userService.getAllUser();
		// 定义PDF视图
		View view = new PdfView(pdfExportService());
		ModelAndView model = new ModelAndView();
		// 设置视图
		model.setView(view);
		// 加入数据模型
		model.addObject("userList", userList);
		return model;
	}

	// 导出PDF定义
	@SuppressWarnings("unchecked")
	private PdfExportService pdfExportService() {
		// TODO Auto-generated method stub
		// 使用Lambda表达式定义自定义导出
		return (model, document, writer, request, response) -> {
			try {
				// A4纸张
				document.setPageSize(PageSize.A4);
				// 标题
				document.addTitle("用户信息");
				// 换行
				document.add(new Chunk("\n"));
				// 表格,3列
				PdfPTable table = new PdfPTable(3);
				// 单元格
				PdfPCell cell = null;
				// 字体,定义为蓝色加粗
				Font f8 = new Font();
				f8.setColor(Color.BLUE);
				f8.setStyle(Font.BOLD);
				// 标题
				cell = new PdfPCell(new Paragraph("id", f8));
				// 居中对齐
				cell.setHorizontalAlignment(1);
				// 将单元格加入表格
				table.addCell(cell);
				cell = new PdfPCell(new Paragraph("user_name", f8));
				// 居中对齐
				cell.setHorizontalAlignment(1);
				table.addCell(cell);
				cell = new PdfPCell(new Paragraph("note", f8));
				cell.setHorizontalAlignment(1);
				table.addCell(cell);
				// 获取数据模型中的用户列表
				List<User> userList = (List<User>) model.get("userList");
				for (User user : userList) {
					document.add(new Chunk("\n"));
					cell = new PdfPCell(new Paragraph(user.getId() + ""));
					table.addCell(cell);
					cell = new PdfPCell(new Paragraph(user.getUser_name()));
					table.addCell(cell);
					String note = user.getNote() == null ? "" : user.getNote();
					cell = new PdfPCell(new Paragraph(note));
					table.addCell(cell);
				}
				// 在文档中加入表格
				document.add(table);
			} catch (DocumentException e) {
				e.printStackTrace();
			}
		};
	}

在这里插入图片描述

文件上传

配置

#上传文件配置
#指定默认上传的文件夹
spring.servlet.multipart.location=E:/springboot
#限制单个文件最大大小,这里设置为5M
spring.servlet.multipart.max-file-size=5242880
#限制所有文件最大大小,这里为20MB
spring.servlet.multipart.max-request-size=20MB

文件上传jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form method="post" action="./multipart" enctype="multipart/form-data">
		<input type="file" name="file" value="请选择上传的文件" /> <input
			type="submit" value="提交" />
	</form>
</body>
</html>

文件上传控制器

package com.springboot.chapter10.controller;
/**imports**/
@Controller
@RequestMapping("/file")
public class FileController {
	//打开上传页面
	@GetMapping("/upload/page")
	public String uploadPage() {
		return "/file/upload";
	}
	
	//使用HttpServletRequest作为参数
	@PostMapping("/upload/request")
	@ResponseBody
	public Map<String, Object> uploadRequest(HttpServletRequest request){
		MultipartHttpServletRequest mreq = null;
		if(request instanceof MultipartHttpServletRequest) {
			//强制转换为MultipartHttpServletRequest接口对象
			mreq = (MultipartHttpServletRequest) request;
		}else {
			return dealResaultMap(false,"上传文件失败!");
		}
		//获取MultipartFile文件信息
		MultipartFile mf = mreq.getFile("file");
		//获取源文件信息
		String filename = mf.getOriginalFilename();
		File file = new File(filename);
		try {
			//保存文件
			mf.transferTo(file);
		} catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			dealResaultMap(false,"上传文件失败!");
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			dealResaultMap(false,"上传文件失败!");
			e.printStackTrace();
		}
		return dealResaultMap(true,"上传文件成功!");
	}
	
	//使用SpringMVC的MultipartFile类作为参数
	@PostMapping("/upload/multipart")
	@ResponseBody
	public Map<String, Object> uploadMultipartFile(MultipartFile file){
		String fileName = file.getOriginalFilename();
		File dest = new File(fileName);
		try {
			file.transferTo(dest);
		} catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			dealResaultMap(false,"上传文件失败!");
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			dealResaultMap(false,"上传文件失败!");
			e.printStackTrace();
		}
		return dealResaultMap(true,"上传文件成功!");
	}
	
	//使用Part类作为参数(推荐)
	@RequestMapping("/upload/part")
	@ResponseBody
	public Map<String, Object> uploadPart(Part file){
		//获取提交文件名称
		String fileName = file.getSubmittedFileName();
		try {
			//写入文件
			file.write(fileName);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			dealResaultMap(false,"上传文件失败!");
			e.printStackTrace();
		}
		return dealResaultMap(true,"上传文件成功!");
	}
	
	//处理上传文件结果
	private Map<String, Object> dealResaultMap(boolean flag,String msg){
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("success", flag);
		map.put("message", msg);
		return map;
	}
}

在这里插入图片描述

拦截器

所有的拦截器都需要实现HanlderInterceptor接口

  1. 自定义拦截器:
package com.springboot.chapter10.interceptor;
/**imports**/
/**
 *自定义简单拦截器
 * @author Administrator
 *
 */
public class Interceptor1 implements HandlerInterceptor {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// TODO Auto-generated method stub
		System.out.println("处理器前方法");
		//返回true不会拦截后续的处理
		return true;
	}
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("处理器后方法");
		HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
	}
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// TODO Auto-generated method stub
		System.out.println("处理器完成方法");
		HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
	}
}

注册拦截器

package com.springboot.chapter10.main;
/**imports**/
/**
 * 实现WebMvcConfigurer接口,覆盖其addInterceptors方法进行注册拦截器
 * @author Administrator
 *
 */
@SpringBootApplication(scanBasePackages = "com.springboot.chapter10")
@MapperScan(basePackages = "com.springboot.chapter10",annotationClass = Mapper.class)
@Configuration
public class Chapter10Application implements WebMvcConfigurer{
	
	public static void main(String[] args) {
		SpringApplication.run(Chapter10Application.class, args);
	}
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		// TODO Auto-generated method stub
		//注册拦截器到springMVC机制,然后他会返回一个拦截器注册
		InterceptorRegistration ir = registry.addInterceptor(new Interceptor1());
		//指定拦截器匹配模式,限制拦截器拦截请求
		ir.addPathPatterns("/interceptor/*");
	}
}

拦截控制器

package com.springboot.chapter10.controller;
/**imports**/
@Controller
@RequestMapping("/interceptor")
public class InterceptorController {
	@GetMapping("/start")
	public String start () {
		System.out.println("执行处理器逻辑");
		return "/welcome";
	}
}

jsp页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>深入Spring MVC</title>
</head>
<body>
 <h1><%
    System.out.println("视图渲染");
    out.print("欢迎学习Spring Boot MVC章节\n");
    %></h1>
</body>
</html>

在这里插入图片描述

  1. 多个拦截器的顺序
    定义多个拦截器,其余几个省略
package com.springboot.chapter10.interceptor;
/**imports**/
/**
 *自定义多个拦截器
 * @author Administrator
 *
 */
public class MulitiInterceptor1 implements HandlerInterceptor {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		// TODO Auto-generated method stub
		System.out.println("【"+this.getClass().getSimpleName()+"】"+"处理器前方法");
		//返回true不会拦截后续的处理
		return true;
	}
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		// TODO Auto-generated method stub
		System.out.println("【"+this.getClass().getSimpleName()+"】"+"处理器后方法");
		HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
	}
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		// TODO Auto-generated method stub
		System.out.println("【"+this.getClass().getSimpleName()+"】"+"处理器完成方法");
		HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
	}
}

注册多个拦截器

@Override
	public void addInterceptors(InterceptorRegistry registry) {
		// TODO Auto-generated method stub
		//注册拦截器到springMVC机制,然后他会返回一个拦截器注册
//		InterceptorRegistration ir = registry.addInterceptor(new Interceptor1());
		//指定拦截器匹配模式,限制拦截器拦截请求
//		ir.addPathPatterns("/interceptor/*");
		
		InterceptorRegistration ir1 = registry.addInterceptor(new MulitiInterceptor1());
		ir1.addPathPatterns("/interceptor/*");
		InterceptorRegistration ir2 = registry.addInterceptor(new MulitiInterceptor2());
		ir2.addPathPatterns("/interceptor/*");
		InterceptorRegistration ir3 = registry.addInterceptor(new MulitiInterceptor3());
		ir3.addPathPatterns("/interceptor/*");
	}

在这里插入图片描述

国际化

配置国际化消息

#国际化配置
#设置国际化消息是否总是采用格式化,默认为false
spring.messages.always-use-message-format=false
#设置国际化属性名称,如果多个可以使用逗号分隔
spring.messages.basename=international
#设置国际化消息缓存超时秒数,默认为永不过期,如果0表示每次都加载
spring.messages.cache-duration=3600
#国际化消息编码
spring.messages.encoding=UTF-8
#如果没有找到特定区域设置的文件,则设置是否返回到系统区域设置
spring.messages.fallback-to-system-locale=true
#是否使用消息编码作为默认的响应消息,而非抛出NoSuchMessageException异常,只建议在开发阶段使用
spring.messages.use-code-as-default-message=false

国际化属性文件:
international_en_US.properties

msg=Spring MVC internationalization

international_zh_CN.properties

msg=Spring MVC\u56FD\u9645\u5316

international.properties

msg=Spring MVC\u56FD\u9645\u5316

添加国际化解析器和拦截器
在springboot的启动类中增加如下代码:

//国际化拦截器
	private LocaleChangeInterceptor lci;
	
	//国际化解析器。注意,这个bean name要为localeResolver
	@Bean(name = "localeResolver")
	public LocaleResolver initLocaleResolver() {
		SessionLocaleResolver slr = new SessionLocaleResolver();
		//默认国际化区域
		slr.setDefaultLocale(Locale.SIMPLIFIED_CHINESE);
		return slr;
	}
	//创建国际化拦截器
	@Bean
	public LocaleChangeInterceptor localeChangeInterceptor() {
		if(lci != null) {
			return lci;
		}
		lci = new LocaleChangeInterceptor();
		//设置参数名
		lci.setParamName("language");
		return lci;
	}

国际化控制器

package com.springboot.chapter10.controller;
/**imports**/
@Controller
@RequestMapping("/i18n")
public class I18nController {
	//注入国际化消息接口对象
	@Autowired
	private MessageSource messageSource;
	
	//后台获取国际化信息和打开国际化视图
	@GetMapping("/page")
	public String page(HttpServletRequest request) {
		//后台获取国际化区域
		Locale locale = LocaleContextHolder.getLocale();
		//获取国际化消息
		String msg = messageSource.getMessage("msg", null, locale);
		System.out.println("msg="+msg);
		//返回视图
		return "/i18n/internationalization";
	}
}

视图国际化

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="mvc" uri="http://www.springframework.org/tags/form"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Spring MVC国际化</title>
</head>
<body>
<!-- 通过HTTP请求参数变化国际化 -->
	<a href="./page?language=zh_CN">简体中文</a>
	<a href="./page?language=en_US">美国英文</a>
	<h2>
		<!-- 找到属性文件变量名为welcome的配置 -->
		<spring:message code="msg" />
	</h2>
	<!-- 当前国际化区域 -->
	Locale: ${pageContext.response.locale }
</body>
</html>

在这里插入图片描述
在这里插入图片描述

SpringMVC拾遗

  1. @ResponseBody注解转换为JSON流程图
    在这里插入图片描述

  2. 重定向

//重定向
	//显示用户
	@GetMapping("/show")
	public String showUser(Integer id,Model model) {
		User user = userService.getUser(id);
		model.addAttribute("user", user);
		return "/data/user";
	}
	
	//使用字符串指定跳转
	@GetMapping("/redirect1")
	public String redirect1(String user_name,String note) {
		User user = new User();
		user.setNote(note);
		user.setUser_name(user_name);
		userService.insert(user);
		System.out.println("user.getId()="+user.getId());
		return "redirect:/user/show?id="+user.getId();
	}
	
	//使用模型和视图指定跳转
	@GetMapping("/redirect2")
	public ModelAndView redirect2(String user_name,String note) {
		User user = new User();
		user.setUser_name(user_name);
		user.setNote(note);
		userService.insert(user);
		ModelAndView mv = new ModelAndView();
		mv.setViewName("redirect:/user/show?id="+user.getId());
		return mv;
	}

重定向传递java对象

//重定向传递java对象
	//显示用户
	@GetMapping("/showUser")
	public String showUser(User user,Model model) {
		System.out.println(user.getId());
		model.addAttribute("user", user);
		return "/data/user";
	}
	
	@RequestMapping("/redirect1")
	public String redirect1(String user_name,String note,RedirectAttributes ra) {
		User user = new User();
		user.setNote(note);
		user.setUser_name(user_name);
		userService.insert(user);
		ra.addFlashAttribute("user", user);
		return "redirect:/user/showUser";
	}
	@RequestMapping("/redirect2")
	public ModelAndView redirect2(String user_name,String note,RedirectAttributes ra) {
		User user = new User();
		user.setNote(note);
		user.setUser_name(user_name);
		userService.insert(user);
		ra.addFlashAttribute("user", user);
		ModelAndView mv = new ModelAndView();
		mv.setViewName("redirect:/user/showUser");
		return mv;
	}
  1. 操作会话对象
    测试操作:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Session</title>
</head>
<body>
<%
	session.setAttribute("id", 1);	//session记录数据
	response.sendRedirect("./test");		//转发URL
%>
</body>
</html>

使用注解@SessionAttributes和@SessionAttribute

package com.springboot.chapter10.controller;
/**imports**/
//@SessionAttributes指定数据模型名称或者属性类型,保存到session中
@SessionAttributes(names = {"user"},types = Integer.class)
@Controller
@RequestMapping("/session")
public class SessionController {
	@Autowired
	private UserService userService;
	
	@RequestMapping("/open")
	public String openSession() {
		return "/session/session1";
	}
	@GetMapping("/test")
	//@SessionAttribute从HttpSession中取出数据,填充控制器方法参数
	public String test(@SessionAttribute("id") Integer id,Model model) {
		User user = userService.getUser(id);
		//根据类型填充到session中
		model.addAttribute("id_new", id);
		//根据名称填充到session中
		model.addAttribute("user", user);
		return "/session/test";
	}
}

测试@SessionAttributes视图

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
    <%@ page import="com.springboot.chapter10.pojo.User" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>展示</title>
</head>
<body>
	<% 
		//从session中获取数据
		Integer id = (Integer)session.getAttribute("id_new");
		User user = (User)session.getAttribute("user");
		//展示数据
		out.print("<br>user_name="+user.getUser_name());
		out.print("<br>user_id="+id);
	%>
</body>
</html>

在这里插入图片描述

  1. 给控制器添加通知
    定义控制器通知:
package com.springboot.chapter10.controller.advice;

import java.text.SimpleDateFormat;
import java.util.Date;

import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
//定义一个控制器通知类
@ControllerAdvice(
		//指定拦截的包
		basePackages = "com.springboot.chapter10.controller.advice.test",
		//限定被标注为@Controller的类才被拦截
		annotations = Controller.class)
public class MyControllerAdvice {
	//绑定格式化、参数转换规则和增加验证器等
	@InitBinder
	public void initDataBinder(WebDataBinder binder) {
		//自定义日期编辑器,限定格式为yyyy-MM-dd,且参数不能为空
		CustomDateEditor dateEditor = new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false);
		//注册自定义日期编辑器
		binder.registerCustomEditor(Date.class, dateEditor);
	}
	//在执行控制器之前先执行,可以初始化数据模型
	@ModelAttribute
	public void projectModel(Model model) {
		model.addAttribute("project_name", "chapter10");
	}
	//异常处理,使得被拦截的控制器方法发生异常时,都能使用相同的视图响应
	@ExceptionHandler(value = Exception.class)
	public String exception(Model model,Exception ex) {
		//给数据模型增加异常信息
		model.addAttribute("exception_message", ex.getMessage());
		//返回异常视图
		return "/exception";
	}
}

测试控制器通知

package com.springboot.chapter10.controller.advice.test;
/**imports**/
@Controller
@RequestMapping("/advice")
public class AdviceController {
	@GetMapping("/test")
	public String test(Date date,ModelMap modelMap) {
		//从数据模型中获取数据
		System.out.println(modelMap.get("project_name"));
		//打印日期参数
	//	System.out.println(DateUtils.format(date,"yyyy-MM-dd"));
		throw new RuntimeException("异常了,跳转到控制器通知的异常信息里"); 
	}
}

展示异常页面

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<h3>${exception_message}</h3>
</body>
</html>

在这里插入图片描述

  1. 获取请求头参数
    注解@RequestHeader
    带请求头的HTTP参数
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>获取请求头参数</title>
<script src="https://code.jquery.com/jquery-3.2.0.js"></script>
<script type="text/javascript">
	$.post({
		url:"./user",
		//设置请求头参数
		headers:{id:'1'},
		success:function(user){
			if(user==null||user.id==null){
				alert("获取失败!");
				return;
			}
			//弹出请求返回的用户信息
			alert("id="+user.id+",user_name="+user.user_name+",note="+user.note);
		}
	});
</script>
</head>
<body>
</body>
</html>

使用@RequestHeader接收请求头参数

//获取请求头参数
	@GetMapping("/header/page")
	public String headerPage() {
		System.out.println("headerPage");
		return "/header";
	}
	@PostMapping("/header/user")
	@ResponseBody
	//用@RequestHeader获取请求头参数
	public User headerUser(@RequestHeader("id") Integer id) {
		System.out.println("id="+id);
		User user = userService.getUser(id);
		System.out.println(user);
		return user;
	}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值