Spring MVC这些接口注解属性你都了解了吗?----ContentType、Accept、header请求头

Spring MVC这些接口注解属性你都了解了吗?


环境:Springboot2.6.12


可消费的内容类型

你可以根据请求的内容类型缩小请求映射范围,如下例所示:


@GetMapping(consumes = MediaType.TEXT_HTML_VALUE)
public Object html() {
  return "html" ;
}
@GetMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
public Object json() {
  return "json" ;
}

html接口会匹配请求的ContentType=text/html
json接口会匹配请求的ContentType=application/json

可生产的内容类型


@GetMapping(produces = MediaType.TEXT_HTML_VALUE)
public Object html() {
  return "<h1>html</h1>" ;
}

@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public Object json() {
  return "json" ;
}

html接口会匹配请求的Accept=text/html
json接口会匹配请求的Accept=application/json

匹配参数值或请求头

两者都有助于对模式进行排序,更具体的模式位于顶部。如果一个模式的URI变量(计为1)、单通配符(计为1)和双通配符(计为2)的计数较低,那么它就不那么具体。如果分数相等,则选择较长的图案。给定相同的分数和长度,将选择URI变量多于通配符的模式。

默认映射模式(/**)被排除在评分之外,并且总是最后排序。此外,前缀模式(例如/public/**)被认为比其他没有双通配符的模式更不具体。

动态注册请求接口

模式比较

当多个模式匹配一个URL时,必须选择最佳匹配。根据是否启用了对已解析PathPattern的使用,可以通过以下方式之一执行此操作:

  • 通过请求参数控制接口

    
    @GetMapping(path = "/params", params = {"name=ak"})
    public Object params() {
      return "params" ;
    }

    当请求的参数中包含name=ak时匹配当前请求

  • 通过请求头控制接口

    
    @GetMapping(path = "/headers", headers = {"token=123"})
    public Object headers() {
      return "headers" ;
    }

    当请求的header中包含token=123时匹配当前请求

    以上根据参数,header等处理的原理如下:

    当一个请求过来后会先从AbstractHandlerMethodMapping

    中的MappingRegistry.pathLookup中查找匹配的RequestMappingInfo对象。

    
    public abstract class AbstractHandlerMethodMapping {
      protected HandlerMethod getHandlerInternal(HttpServletRequest request) throws Exception {
        String lookupPath = initLookupPath(request);
        HandlerMethod handlerMethod = lookupHandlerMethod(lookupPath, request);
      }  
      protected HandlerMethod lookupHandlerMethod(String lookupPath, HttpServletRequest request) throws Exception {
        List<Match> matches = new ArrayList<>();
        List<T> directPathMatches = this.mappingRegistry.getMappingsByDirectPath(lookupPath);
        if (directPathMatches != null) {
          addMatchingMappings(directPathMatches, matches, request);
        }
      }
      private void addMatchingMappings(Collection<T> mappings, List<Match> matches, HttpServletRequest request) {
        for (T mapping : mappings) {
          // 从匹配的集合中再根据每个映射的条件配置匹配;进入子类RequestMappingInfoHandlerMapping 中
          T match = getMatchingMapping(mapping, request);
          if (match != null) {
            matches.add(new Match(match, this.mappingRegistry.getRegistrations().get(mapping)));
          }
        }
      }
    }
    public abstract class RequestMappingInfoHandlerMapping {
      protected RequestMappingInfo getMatchingMapping(RequestMappingInfo info, HttpServletRequest request) {
        return info.getMatchingCondition(request);
      }
    }
    public final class RequestMappingInfo {
      public RequestMappingInfo getMatchingCondition(HttpServletRequest request) {
        RequestMethodsRequestCondition methods = this.methodsCondition.getMatchingCondition(request);
        if (methods == null) {
          return null;
        }
        ParamsRequestCondition params = this.paramsCondition.getMatchingCondition(request);
        if (params == null) {
          return null;
        }
        HeadersRequestCondition headers = this.headersCondition.getMatchingCondition(request);
        if (headers == null) {
          return null;
        }
        ConsumesRequestCondition consumes = this.consumesCondition.getMatchingCondition(request);
        if (consumes == null) {
          return null;
        }
        ProducesRequestCondition produces = this.producesCondition.getMatchingCondition(request);
        if (produces == null) {
          return null;
        }
        PathPatternsRequestCondition pathPatterns = null;
        if (this.pathPatternsCondition != null) {
          pathPatterns = this.pathPatternsCondition.getMatchingCondition(request);
          if (pathPatterns == null) {
            return null;
          }
        }
        PatternsRequestCondition patterns = null;
        if (this.patternsCondition != null) {
          patterns = this.patternsCondition.getMatchingCondition(request);
          if (patterns == null) {
            return null;
          }
        }
        RequestConditionHolder custom = this.customConditionHolder.getMatchingCondition(request);
        if (custom == null) {
          return null;
        }
        return new RequestMappingInfo(this.name, pathPatterns, patterns,
            methods, params, headers, consumes, produces, custom, this.options);
      }  
    }

    URI模式

    @RequestMapping 使用URL模式映射,有两种选择:

    PathPattern:与URL路径匹配的预解析模式也预解析为PathContainer。此解决方案专为web使用而设计,可有效处理编码和路径参数,并进行高效匹配。

    AntPathMatcher:根据字符串路径匹配字符串模式。这是最初的解决方案,也是在Spring配置中用于选择类路径、文件系统和其他位置上的资源。它的效率较低,而且字符串路径输入对于有效处理URL的编码和其他问题是一个挑战。

    PathPattern是web应用程序的推荐解决方案,也是SpringWebFlux中的唯一选择。在版本5.3之前,AntPathMatcher是SpringMVC中唯一的选择,并且仍然是默认的。但是,可以在中启用PathPattern。

    PathPattern支持与AntPathMatcher相同的模式语法。此外,它还支持捕获模式,例如{*spring},用于在路径末尾匹配0个或多个路径段。PathPattern还限制使用**来匹配多个路径段,因此只允许在模式的末尾使用**。这消除了为给定请求选择最佳匹配模式时出现的许多模糊情况。有关完整的模式语法,请参阅PathPattern和AntPathMatcher。

    一些示例模式:

  • /resources/ima?e.png - 匹配路径段中的一个字符

  • /resources/*.png - 匹配路径段中的零个或多个字符

  • /resources/** - 匹配多个路径段

  • /projects/{project}/versions - 匹配路径段并将其捕获为变量

  • /projects/{project:[a-z]+}/versions - 使用正则表达式匹配并捕获变量

  • PathPattern.SPECIFICITY_COMPARATOR

  • AntPathMatcher.getPatternComparator(String path)

两者都有助于对模式进行排序,更具体的模式位于顶部。如果一个模式的URI变量(计为1)、单通配符(计为1)和双通配符(计为2)的计数较低,那么它就不那么具体。如果分数相等,则选择较长的图案。给定相同的分数和长度,将选择URI变量多于通配符的模式。

默认映射模式(/**)被排除在评分之外,并且总是最后排序。此外,前缀模式(例如/public/**)被认为比其他没有双通配符的模式更不具体。

动态注册请求接口

@Service
public class UserHandler {

  @ResponseBody
  public Object getUsers(@PathVariable("id") String id, HttpServletRequest request) {
    System.out.println(request) ;
    return "查询用户ID为:" + id ;
  }
}

 你的处理程序可以不是受容器管理的Bean。这里还应用了SpringMVC相关的一些注解,这些注解都可以像Controller中使用一样。


@Configuration
public class MappingConfig {
  @Autowired
  public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) throws NoSuchMethodException {
    RequestMappingInfo info = RequestMappingInfo.paths("/users/{id}").methods(RequestMethod.GET).build();
    Method method = UserHandler.class.getMethod("getUsers", String.class, HttpServletRequest.class);
    mapping.registerMapping(info, handler, method);
  }
}

创建RequestMappingInfo对象,就是一些请求的基本元信息。获取处理程序的方法对象。通过RequestMappingHandlerMapping注册请求映射对象。

完毕!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值