目录
一、请求参数处理
1.1、请求映射
1.2、自定义请求规则
1.3、请求处理
1.4、普通参数与基本注解
1.4.1、注解
1.5、参数处理原则
1.6、复杂参数
1.7、自定义参数对象
1.8、自定义Converter
一、请求参数处理
1.1、请求映射
// @RequestMapping(value = "/user",method = RequestMethod.GET)
@GetMapping("/user")
public String getUser(){
return "GET-张三";
}
// @RequestMapping(value = "/user",method = RequestMethod.POST)
@PostMapping("/user")
public String saveUser(){
return "POST-张三";
}
//@RequestMapping(value = "/user",method = RequestMethod.PUT)
@PutMapping("/user")
public String putUser(){
return "PUT-张三";
}
//@RequestMapping(value = "/user",method = RequestMethod.DELETE)
@DeleteMapping("/user")
public String deleteUser(){
return "DELETE-张三";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>你好,老莫</h1>
<form action="/user" method="get">
<input value="get提交" type="submit">
</form>
<form action="/user" method="post">
<input value="post提交" type="submit">
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="DELETE">
<input value="delete提交" type="submit">
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="PUT">
<input value="put提交" type="submit">
</form>
</body>
</html>
需要手动开启rest风格
原因是框架底层默认没有开启
@Bean
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
@ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}
rest原理(表单提交时):
1、表单提交会带上_method=put
2、请求过来会被HiddenHttpMethodFilter拦截:①、请求是否正常,并且是post。②、获取到_method的值。③、兼容PUT、DELETE、PATCH。④、原生request(post),包装requestWrapper重写了getMethod方法,返回的是传入的值。⑤、过滤器链放行的时候用wrapper,后面的方法调用getMethod是调用requestWrapper的。
3、使用客户端工具如:postman就不会过filter
1.2、自定义请求规则
@Configuration(proxyBeanMethods = false)
public class WebConfig {
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
hiddenHttpMethodFilter.setMethodParam("_k");
return hiddenHttpMethodFilter;
}
}
1.3、请求处理
springmvc功能分析都从org\springframework\web\servlet\DispatcherServlet.java -----》doDispatch()
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); // Determine handler for the current request. //找到当前请求使用哪个Handler(Controller的方法)处理 mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; }
handlerMapping:处理器映射
RequestMappingHandlerMapping:保存了所有@RequestMapping和handler的映射规则。
所有的请求映射在HandlerMapping中,springboot自动配置欢迎页的WelcomePageHandlerMapping,访问/能访问到index.html。请求进来,会按个尝试所有HandlerMapping看是否有请求信息,如果有就找到这个请求对应的handler,如果没有就到下一个HandlerMapping。
1.4、普通参数与基本注解
1.4.1、注解
@RestController
public class ParameterController {
/**
* @PathVariable 路径变量
* @RequestHeader 获取请求头
* @RequestParam 获取请求参数
* @CookieValue 获取cookie的值
* @param id
* @param name
* @param pv
* @return
*/
@GetMapping("/map/{id}/owner/{name}")
public Map<String,Object> map(@PathVariable("id") Integer id,
@PathVariable("name") String name,
@PathVariable Map<String ,String > pv,
@RequestHeader("User-Agent") String userAgent,
@RequestHeader Map<String ,String > header,
@RequestParam("age") Integer age,
@RequestParam("inters")List<String> inters,
@RequestParam Map<String,String> params){
HashMap<String, Object> map = new HashMap<>();
// map.put("id",id);
// map.put("name",name);
// map.put("pv",pv);
// map.put("userAgent",userAgent);
// map.put("header",header);
map.put("age",age);
map.put("inters",inters);
map.put("params",params);
return map;
}
/**
* @RequestBody 获取请求体[post]
* @param content
* @return
*/
@PostMapping("/save")
public Map postMap(@RequestBody String content){
HashMap<String, Object> map = new HashMap<>();
map.put("content",content);
return map;
}
/**
*@MatrixVariable:矩阵变量
* @param low
* @param brand
* @return
* springboot默认是禁用了矩阵变量的功能
* 手动开启:
* 1、对于路径的处理:UrlPathHelper进行解析
* 2、removeSemicolonContent:支持矩阵变量
*/
@GetMapping("/car/{path}")
public Map sell(@MatrixVariable("low") Integer low,
@MatrixVariable("brand") List<String> brand,
@PathVariable String path){
HashMap<String , Object> map = new HashMap<>();
map.put("low",low);
map.put("brand",brand);
map.put("path",path);
return map;
}
@GetMapping("/boss/{bossId}/{empId}")
public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossId,
@MatrixVariable(value = "age",pathVar = "empId") Integer empId){
HashMap<Object, Object> map = new HashMap<>();
map.put("bossId",bossId);
map.put("empId",empId);
return map;
}
}
@Controller
public class RequestController {
@GetMapping("/goto")
public String gotoPage(HttpServletRequest request){
request.setAttribute("msg","转发成功");
return "forward:/success";//转发到/success请求
}
/**
* RequestAttribute :获取request域属性
* @param msg
* @param request
* @return
*/
@ResponseBody
@GetMapping("/success")
public Map success(@RequestAttribute("msg") String msg,
HttpServletRequest request){
HashMap<String , Object> map = new HashMap<>();
map.put("request-msg",request.getAttribute("msg"));
map.put("requestAtt_msg",msg);
return map;
}
}
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
//不移除分号内容,矩阵变量功能生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
};
}
// @Override
// public void configurePathMatch(PathMatchConfigurer configurer) {
// UrlPathHelper urlPathHelper = new UrlPathHelper();
// //不移除分号内容,矩阵变量功能生效
// urlPathHelper.setRemoveSemicolonContent(false);
// configurer.setUrlPathHelper(urlPathHelper);
// }
}
1.5、参数处理原则
1、HandlerMapping中找到能处理请求的Handler(Controller中的方法)。
2、在当前Handler中找到一个HandlerAdapter
HandlerAdapter:
RequestMappingHandlerAdapter:支持方法上标注@RequestMapping
HandlerFunctionAdapter:支持函数式编程。
执行目标方法
// Actually invoke the handler. //DispatcherServlet ---》doDispatch mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
//执行目标方法
mav = invokeHandlerMethod(request, response, handlerMethod);
//真正执行目标方法
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
//获取方法的参数值
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
参数解析器
确定将要执行的目标方法的每一个参数的值
SpringMVC目标方法能写多少种参数类型,取决于参数解析器。
当前解析器是否支持解析这种参数
支持就调用resolveArgument
返回值处理器
确定目标方法每一个参数的值
protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer, Object... providedArgs) throws Exception { MethodParameter[] parameters = getMethodParameters(); if (ObjectUtils.isEmpty(parameters)) { return EMPTY_ARGS; } Object[] args = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { MethodParameter parameter = parameters[i]; parameter.initParameterNameDiscovery(this.parameterNameDiscoverer); args[i] = findProvidedArgument(parameter, providedArgs); if (args[i] != null) { continue; } if (!this.resolvers.supportsParameter(parameter)) { throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver")); } try { args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory); } catch (Exception ex) { // Leave stack trace for later, exception may actually be resolved and handled... if (logger.isDebugEnabled()) { String exMsg = ex.getMessage(); if (exMsg != null && !exMsg.contains(parameter.getExecutable().toGenericString())) { logger.debug(formatArgumentError(parameter, exMsg)); } } throw ex; } } return args; }
在所有参数解析器中判断哪个支持当前参数
@Nullable private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) { if (resolver.supportsParameter(parameter)) { result = resolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; }
解析这个参数的值
调用HandlerMethodArgumentResolver的resolveArgument方法
1.6、复杂参数
@ResponseBody
@GetMapping("/success")
public Map success(HttpServletRequest request){
HashMap<String , Object> map = new HashMap<>();
Object mapkey = request.getAttribute("mapkey");
Object modelkey = request.getAttribute("modelkey");
Object requestkey = request.getAttribute("requestkey");
map.put("mapkey",mapkey);
map.put("modelkey",modelkey);
map.put("requestkey",requestkey);
return map;
}
/**
* 这三个都可以在request域中放数据
* @param map
* @param model
* @param request
*
* @param response
* @return
*/
@GetMapping("/param")
public String param(Map<String,Object> map,
Model model,
HttpServletRequest request,
HttpServletResponse response){
map.put("mapkey","mapvalue");
model.addAttribute("modelkey","modelvalue");
request.setAttribute("requestkey","requestvalue");
Cookie cookie = new Cookie("key1","value1");
response.addCookie(cookie);
return "forward:/success";
}
1.7、自定义参数对象
@Data
public class Persons {
private String userName;
private Date birth;
private Integer age;
private Pets pets;
}
@Data
public class Pets {
private String name;
private Integer age;
}
/**
* 数据绑定:页面提交的请求数据(GET,POST)都可以和对象属性进行绑定
* @param persons
* @return
*/
@PostMapping("/saveuser")
public Persons saveuser(Persons persons){
return persons;
}
<form action="/saveuser" method="post">
姓名:<input name="userName" value="老莫"/></br>
年龄:<input name="age" value="12"/></br>
生日:<input name="birth" value="2011/10/1"/></br>
宠物名:<input name="pets.name" value="金龙鱼"/></br>
宠物年龄:<input name="pets.age" value="2"/></br>
<input value="保存" type="submit">
</form>
ServletModelAttributeMethodProcessor 这个参数处理器支持
是否是简单类型
public static boolean isSimpleValueType(Class<?> type) { return (Void.class != type && void.class != type && (ClassUtils.isPrimitiveOrWrapper(type) || Enum.class.isAssignableFrom(type) || CharSequence.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) || Date.class.isAssignableFrom(type) || Temporal.class.isAssignableFrom(type) || URI.class == type || URL.class == type || Locale.class == type || Class.class == type)); }
@Override @Nullable public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception { Assert.state(mavContainer != null, "ModelAttributeMethodProcessor requires ModelAndViewContainer"); Assert.state(binderFactory != null, "ModelAttributeMethodProcessor requires WebDataBinderFactory"); String name = ModelFactory.getNameForParameter(parameter); ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class); if (ann != null) { mavContainer.setBinding(name, ann.binding()); } Object attribute = null; BindingResult bindingResult = null; if (mavContainer.containsAttribute(name)) { attribute = mavContainer.getModel().get(name); } else { // Create attribute instance try { attribute = createAttribute(name, parameter, binderFactory, webRequest); } catch (BindException ex) { if (isBindExceptionRequired(parameter)) { // No BindingResult parameter -> fail with BindException throw ex; } // Otherwise, expose null/empty value and associated BindingResult if (parameter.getParameterType() == Optional.class) { attribute = Optional.empty(); } bindingResult = ex.getBindingResult(); } } if (bindingResult == null) { // Bean property binding and validation; // skipped in case of binding failure on construction. WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name); if (binder.getTarget() != null) { if (!mavContainer.isBindingDisabled(name)) { bindRequestParameters(binder, webRequest); } validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { throw new BindException(binder.getBindingResult()); } } // Value type adaptation, also covering java.util.Optional if (!parameter.getParameterType().isInstance(attribute)) { attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter); } bindingResult = binder.getBindingResult(); } // Add resolved attribute and BindingResult at the end of the model Map<String, Object> bindingResultModel = bindingResult.getModel(); mavContainer.removeAttributes(bindingResultModel); mavContainer.addAllAttributes(bindingResultModel); return attribute; }
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
WebDataBinder:web数据绑定器,将请求参数的值绑定到指定的JavaBean里面
WebDataBinder利用它里面的Converters将请求数据转成指定的数据类型,再次封装到JavaBean中。
GenericConversionService:在设置每一个值的时候,找到它里面所有的converter那个可以将这个数据类型(request带来参数的字符串)转换到指定的类型(JavaBean ----》Integer).
1.8、自定义Converter
<form action="/saveuser" method="post">
姓名:<input name="userName" value="老莫"/></br>
年龄:<input name="age" value="12"/></br>
生日:<input name="birth" value="2011/10/1"/></br>
<!-- 宠物名:<input name="pets.name" value="金龙鱼"/></br>-->
<!-- 宠物年龄:<input name="pets.age" value="2"/></br>-->
宠物:<input name="pets" value="金龙鱼,3">
<input value="保存" type="submit">
</form>
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
//不移除分号内容,矩阵变量功能生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter<String , Pets>() {
@Override
public Pets convert(String source) {
if (!StringUtils.isEmpty(source)){
Pets pets = new Pets();
String[] split = source.split(",");
pets.setName(split[0]);
pets.setAge(Integer.valueOf(split[1]));
return pets;
}
return null;
}
});
}
};
}