请求处理 1.Rest 映射及源码解析 请求映射 用法 开启页面表单的 Rest 功能 页面 form 的属性 method=post,隐藏域 _method=put、delete 等(如果直接 get 或 post,无需隐藏域) 编写请求映射 1 2 3 4 5 spring: mvc: hidden method: filter: enabled: true
index 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <!DOCTYPE html > <html lang ="en" > <head > <meta charset ="UTF-8" /> <meta name ="viewport" content ="width=device-width, initial-scale=1.0" /> <title > REST Form Submissions</title > </head > <body > <form action ="/user" method ="get" > <input value ="REST-GET提交" type ="submit" /> </form > <form action ="/user" method ="post" > <input value ="REST-POST提交" type ="submit" /> </form > <form action ="/user" method ="post" > <input name ="_method" type ="hidden" value ="DELETE" /> <input value ="REST-DELETE 提交" type ="submit" /> </form > <form action ="/user" method ="post" > <input name ="_method" type ="hidden" value ="PUT" /> <input value ="REST-PUT提交" type ="submit" /> </form > </body > </html >
HelloController 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @RequestMapping(value = "/user", method = RequestMethod.GET) public String getUser () { return "GET-张三" ; } @RequestMapping(value = "/user", method = RequestMethod.POST) public String postUser () { return "POST-张三" ; } @RequestMapping(value = "/user", method = RequestMethod.PUT) public String putUser () { return "PUT-张三" ; } @RequestMapping(value = "/user", method = RequestMethod.DELETE) public String deleteUser () { return "DELETE-张三" ; }
Rest 原理(表单提交要使用 REST 的时候)
表单提交会带上\_method=PUT
请求过来被 HiddenHttpMethodFilter
拦截请求是否正常,并且是 POST获取到\_method
的值。 兼容以下请求;PUT .DELETE .PATCH 原生 request(post),包装模式 requesWrapper 重写了 getMethod 方法,返回的是传入的值。 过滤器链放行的时候用 wrapper。以后的方法调用 getMethod 是调用 requesWrapper 的。 HiddenHttpMethodFilter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public class HiddenHttpMethodFilter extends OncePerRequestFilter { private static final List<String> ALLOWED_METHODS = Collections.unmodifiableList(Arrays.asList(HttpMethod.PUT.name(), HttpMethod.DELETE.name(), HttpMethod.PATCH.name())); public static final String DEFAULT_METHOD_PARAM = "_method" ; private String methodParam = DEFAULT_METHOD_PARAM; public void setMethodParam (String methodParam) { Assert.hasText(methodParam, "'methodParam' must not be empty" ); this .methodParam = methodParam; } @Override protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpServletRequest requestToUse = request; if ("POST" .equals(request.getMethod()) && request.getAttribute(WebUtils.ERROR_EXCEPTION_ATTRIBUTE) == null ) { String paramValue = request.getParameter(this .methodParam); if (StringUtils.hasLength(paramValue)) { String method = paramValue.toUpperCase(Locale.ENGLISH); if (ALLOWED_METHODS.contains(method)) { requestToUse = new HttpMethodRequestWrapper (request, method); } } } filterChain.doFilter(requestToUse, response); } private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper { private final String method; public HttpMethodRequestWrapper (HttpServletRequest request, String method) { super (request); this .method = method; } @Override public String getMethod () { return this .method; } } }
Rest使用客户端工具。
如PostMan可直接发送put、delete等方式请求。 2.怎么改变默认的_method WebMvcAutoConfiguration 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Configuration(proxyBeanMethods = false) @ConditionalOnWebApplication(type = Type.SERVLET) @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class }) @ConditionalOnMissingBean(WebMvcConfigurationSupport.class) @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration { ... @Bean @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false) public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter () { return new OrderedHiddenHttpMethodFilter (); } ... }
@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)
意味着在没有HiddenHttpMethodFilter
时,才执行hiddenHttpMethodFilter()
。因此,我们可以自定义filter,改变默认的\_method
。例如:
1 2 3 4 5 6 7 8 9 10 @Configuration(proxyBeanMethods = false) public class WebConfig { @Bean public HiddenHttpMethodFilter hiddenHttpMethodFilter () { HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter (); methodFilter.setMethodParam("_m" ); return methodFilter; } }
将\_method
改成_m
。
1 2 3 4 5 6 7 8 9 <form action ="/user" method ="post" > <input name ="_method" type ="hidden" value ="DELETE" /> <input value ="REST-DELETE 提交" type ="submit" /> </form > <form action ="/user" method ="post" > <input name ="_method" type ="hidden" value ="PUT" /> <input value ="REST-PUT提交" type ="submit" /> </form >
3.请求映射原理
SpringMVC功能分析都从 org.springframework.web.servlet.DispatcherServlet
-> doDispatch()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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); mappedHandler = getHandler(processedRequest); ... }
getHandler()
方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 @Nullable protected HandlerExecutionChain getHandler (HttpServletRequest request) throws Exception { if (this .handlerMappings != null ) { for (HandlerMapping mapping : this .handlerMappings) { HandlerExecutionChain handler = mapping.getHandler(request); if (handler != null ) { return handler; } } } return null ; }
this.handlerMappings
在Debug模式下展现的内容: 其中,保存了所有@RequestMapping
和handler
的映射规则。
所有的请求映射都在HandlerMapping中:
SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。
如果有就找到这个请求对应的handler 如果没有就是下一个 HandlerMapping 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping 。自定义 HandlerMapping
IDEA快捷键:
Ctrl + Alt + U : 以UML的类图展现类有哪些继承类,派生类以及实现哪些接口。 Crtl + Alt + Shift + U : 同上,区别在于上条快捷键结果在新页展现,而本条快捷键结果在弹窗展现。 Ctrl + H : 以树形方式展现类层次结构图。 4.常用参数注解使用 注解:
@PathVariable
路径变量@RequestHeader
获取请求头@RequestParam
获取请求参数(指问号后的参数,url?a=1&b=2)@CookieValue
获取Cookie值@RequestAttribute
获取request域属性@RequestBody
获取请求体[POST]@MatrixVariable
矩阵变量@ModelAttribute
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 @RestController public class ParameterTestController { @GetMapping("/car/{id}/owner/{username}") public Map<String,Object> getCar (@PathVariable("id") Integer id, @PathVariable("username") 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, @CookieValue("_ga") String _ga, @CookieValue("_ga") Cookie cookie) { Map<String,Object> map = new HashMap <>(); map.put("age" ,age); map.put("inters" ,inters); map.put("params" ,params); map.put("_ga" ,_ga); System.out.println(cookie.getName()+"===>" +cookie.getValue()); return map; } @PostMapping("/save") public Map postMethod (@RequestBody String content) { Map<String,Object> map = new HashMap <>(); map.put("content" ,content); return map; } }
5.@RequestAttribute 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @Controller public class RequestController { @GetMapping("/goto") public String goToPage (HttpServletRequest request) { request.setAttribute("msg" ,"成功了..." ); request.setAttribute("code" ,200 ); return "forward:/success" ; } @GetMapping("/params") public String testParam (Map<String,Object> map, Model model, HttpServletRequest request, HttpServletResponse response) { map.put("hello" ,"world666" ); model.addAttribute("world" ,"hello666" ); request.setAttribute("message" ,"HelloWorld" ); Cookie cookie = new Cookie ("c1" ,"v1" ); response.addCookie(cookie); return "forward:/success" ; } @ResponseBody @GetMapping("/success") public Map success (@RequestAttribute(value = "msg",required = false) String msg, @RequestAttribute(value = "code",required = false) Integer code, HttpServletRequest request) { Object msg1 = request.getAttribute("msg" ); Map<String,Object> map = new HashMap <>(); Object hello = request.getAttribute("hello" ); Object world = request.getAttribute("world" ); Object message = request.getAttribute("message" ); map.put("reqMethod_msg" ,msg1); map.put("annotation_msg" ,msg); map.put("hello" ,hello); map.put("world" ,world); map.put("message" ,message); return map; } }
6.@MatrixVariable与UrlPathHelper 语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd
SpringBoot默认是禁用了矩阵变量的功能
手动开启:原理。对于路径的处理。UrlPathHelper的removeSemicolonContent设置为false,让其支持矩阵变量的。 矩阵变量必须 有url路径变量才能被解析
手动开启矩阵变量 :1 2 3 4 5 6 7 8 9 10 11 @Configuration(proxyBeanMethods = false) public class WebConfig implements WebMvcConfigurer { @Override public void configurePathMatch (PathMatchConfigurer configurer) { UrlPathHelper urlPathHelper = new UrlPathHelper (); urlPathHelper.setRemoveSemicolonContent(false ); configurer.setUrlPathHelper(urlPathHelper); } }
创建返回WebMvcConfigurer
Bean: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration(proxyBeanMethods = false) public class WebConfig { @Bean public WebMvcConfigurer webMvcConfigurer () { return new WebMvcConfigurer () { @Override public void configurePathMatch (PathMatchConfigurer configurer) { UrlPathHelper urlPathHelper = new UrlPathHelper (); urlPathHelper.setRemoveSemicolonContent(false ); configurer.setUrlPathHelper(urlPathHelper); } } } }
@MatrixVariable
的用例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 @RestController public class ParameterTestController { @GetMapping("/cars/{path}") public Map carsSell (@MatrixVariable("low") Integer low, @MatrixVariable("brand") List<String> brand, @PathVariable("path") String path) { Map<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 bossAge, @MatrixVariable(value = "age",pathVar = "empId") Integer empAge) { Map<String,Object> map = new HashMap <>(); map.put("bossAge" ,bossAge); map.put("empAge" ,empAge); return map; } }