文章目录
- 请求处理
- 1.Rest 映射及源码解析
- 请求映射
- 用法
- index
- HelloController
- HiddenHttpMethodFilter
- 2.怎么改变默认的\_method
- WebMvcAutoConfiguration
- 3.请求映射原理
- 4.常用参数注解使用
- 5.@RequestAttribute
- 6.@MatrixVariable与UrlPathHelper
- **手动开启矩阵变量**:
请求处理
1.Rest 映射及源码解析
请求映射
-
@xxxMapping;
- @GetMapping
- @PostMapping
- @PutMapping
- @DeleteMapping
-
Rest 风格支持(使用HTTP请求方式动词来表示对资源的操作)
- 以前:
- /getUser 获取用户
- /deleteUser 删除用户
- /editUser 修改用户
- /saveUser 保存用户
- 现在: /user
- GET-获取用户
- DELETE-删除用户
- PUT-修改用户
- POST-保存用户
- 核心 Filter;HiddenHttpMethodFilter
- 以前:
用法
- 开启页面表单的 Rest 功能
- 页面 form 的属性 method=post,隐藏域 _method=put、delete 等(如果直接 get 或 post,无需隐藏域)
- 编写请求映射
spring:mvc:hidden method:filter:enabled: true #开启页面表单的Rest功能
index
<!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
@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 的。
- 获取到
- 请求是否正常,并且是 POST
HiddenHttpMethodFilter
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()));/** Default method parameter: {@code _method}. */public static final String DEFAULT_METHOD_PARAM = "_method";private String methodParam = DEFAULT_METHOD_PARAM;/*** Set the parameter name to look for HTTP methods.* @see #DEFAULT_METHOD_PARAM*/public void setMethodParam(String methodParam) {Assert.hasText(methodParam, "'methodParam' must not be empty");this.methodParam = methodParam;}@Overrideprotected 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);}/*** Simple {@link HttpServletRequest} wrapper that returns the supplied method for* {@link HttpServletRequest#getMethod()}.*/private static class HttpMethodRequestWrapper extends HttpServletRequestWrapper {private final String method;public HttpMethodRequestWrapper(HttpServletRequest request, String method) {super(request);this.method = method;}@Overridepublic String getMethod() {return this.method;}}}
Rest使用客户端工具。
- 如PostMan可直接发送put、delete等方式请求。
2.怎么改变默认的_method
WebMvcAutoConfiguration
@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
。例如:
@Configuration(proxyBeanMethods = false)
public class WebConfig{//自定义filter@Beanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter(){HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();methodFilter.setMethodParam("_m");return methodFilter;}
}
将\_method
改成_m
。
<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()
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);// 找到当前请求使用哪个Handler(Controller的方法)处理mappedHandler = getHandler(processedRequest);//HandlerMapping:处理器映射。/xxx->>xxxx...
}
getHandler()
方法如下:
@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
@RestController
public class ParameterTestController {// car/2/owner/zhangsan@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("id",id);
// map.put("name",name);
// map.put("pv",pv);
// map.put("userAgent",userAgent);
// map.put("headers",header);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
@Controller
public class RequestController {@GetMapping("/goto")public String goToPage(HttpServletRequest request){request.setAttribute("msg","成功了...");request.setAttribute("code",200);return "forward:/success"; //转发到 /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";}///<-----------------主角@RequestAttribute在这个方法@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路径变量才能被解析
手动开启矩阵变量:
- 实现
WebMvcConfigurer
接口:
@Configuration(proxyBeanMethods = false)
public class WebConfig implements WebMvcConfigurer {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();// 不移除;后面的内容。矩阵变量功能就可以生效urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}
}
- 创建返回
WebMvcConfigurer
Bean:
@Configuration(proxyBeanMethods = false)
public class WebConfig{@Beanpublic WebMvcConfigurer webMvcConfigurer(){return new WebMvcConfigurer() {@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {UrlPathHelper urlPathHelper = new UrlPathHelper();// 不移除;后面的内容。矩阵变量功能就可以生效urlPathHelper.setRemoveSemicolonContent(false);configurer.setUrlPathHelper(urlPathHelper);}}}
}
@MatrixVariable
的用例
@RestController
public class ParameterTestController {///cars/sell;low=34;brand=byd,audi,yd@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;}// /boss/1;age=20/2;age=10@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;}}