1. 请求映射
-
@xxxMapping:
-
Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
-
以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
-
现在:/user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
-
核心Filter:HiddenHttpMethodFilter
-
用法:表单method=post,隐藏域_method=put
-
需要在SpringBoot中手动开启
-
-
-
@Bean @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled") public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() { return new OrderedHiddenHttpMethodFilter(); }
spring: mvc: hiddenmethod: filter: enable: true #开启页面表单的Rest风格
Rest原理(表单提交使用Rest风格时)
-
表单提交会带上_method=PUT
-
请求过来被HiddenHttpMethodFilter拦截
-
请求是否正常,并且是POST
-
获取_method的值
-
兼容以下请求:PUT、DELETE、PATCH
-
原生request(post),包装模式requestWrapper重写了getMethod()方法,返回的是传入的值
-
过滤器链放行的时候用wrapper作为request放行,以后的方法调用getMethod()是调用requestWrapper的
-
-
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpServletRequest requestToUse = request; // 表单上的方法必须声明为POST if ("POST".equals(request.getMethod()) && request.getAttribute("javax.servlet.error.exception") == null) { // 获得_method请求参数 String paramValue = request.getParameter(this.methodParam); // 是否为空 if (StringUtils.hasLength(paramValue)) { // 不为空,将字符串转换成大写,所以在前端声明时大小写都可以 String method = paramValue.toUpperCase(Locale.ENGLISH); // 判断允许的请求方式中是否包含该请求方式 if (ALLOWED_METHODS.contains(method)) { requestToUse = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method); } } } filterChain.doFilter((ServletRequest)requestToUse, response); }
修改请求方式参数名
// 组件之间没有依赖 @Configuration(proxyBeanMethods = false) public class WebConfig { @Bean public HiddenHttpMethodFilter hiddenHttpMethodFilter(){ HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter(); // 修改请求方式参数名 methodFilter.setMethodParam("_m"); return methodFilter; } }
Rest使用客户端工具
-
如axios、PostMan等直接发送put、delete等方式请求,无需filter开启
请求映射
继承关系
请求进来,先调用HttpServlet的doGet()方法,在doGet方法中会调用FrameworkServlet的processRequest(HttpServletRequest request, HttpServletResponse response)方法,在processRequest中调用DispatcherServlet的doService()方法,再调用doDispatch(每个请求都会调用)
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处理当前请求 // Determine handler for the current request. mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; }
// HanlderMapping,处理器映射 /xxx->xxxController->method // Request Mapping HandlerMapping:保存了所有@RequestMapping 和 handler的映射规则 @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; }
所有的请求映射都保存在Handler Mapping中
-
SpringBoot自己注册了欢迎页的请求映射"/"可以访问index.html
-
SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
-
请求进来,尝试所有的HanlderMapping看是否有请求信息
-
如果有就找到这个请求对应的handler
-
如果没有就是下一个HandlerMapping
-
-
我们需要一些自定义的映射处理,我们也可自己给容器中注册自定义的HandlerMapping