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开启
请求映射

继承关系

spring 如何手动设置请求头 springboot请求头_sed

请求进来,先调用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;
    }

spring 如何手动设置请求头 springboot请求头_后端_02

所有的请求映射都保存在Handler Mapping中

  • SpringBoot自己注册了欢迎页的请求映射"/"可以访问index.html
  • SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
  • 请求进来,尝试所有的HanlderMapping看是否有请求信息
  • 如果有就找到这个请求对应的handler
  • 如果没有就是下一个HandlerMapping
  • 我们需要一些自定义的映射处理,我们也可自己给容器中注册自定义的HandlerMapping