代码之家  ›  专栏  ›  技术社区  ›  dzikoysk Shan

ResourceHandler与控制器中的通配符冲突

  •  5
  • dzikoysk Shan  · 技术社区  · 6 年前

    我想保留所有的路径 /static/** 对于资源处理程序。 不幸的是,我有一些从根路径派生的通配符 / 在请求映射中。像这样的:

    Preview

    我试了什么?

    • ResourceHandlerRegistry#setOrder :

      @Override
      public void addResourceHandlers(ResourceHandlerRegistry registry) {
          registry.addResourceHandler("/static/**")
                  .addResourceLocations("classpath:/resources/static/");
      
          registry.setOrder(1);
      }
      
    • 拦截器的各种版本(有或没有命令):

      @Override
      public void addInterceptors(InterceptorRegistry registry) {
          registry.addInterceptor(new ResourcesInterceptor())
                  .excludePathPatterns("/static/**")
                  .order(2);
      }
      

    这是一个半心半意的成功(如果我将映射更改为 /{profile}/{project}/** ),因为:

    /static/style/home.css      # works
    /static/style/home.cssxxx   # 404, works
    /static/style               # catched by controller, expected: 404
    /static                     # catched by controller, expected: 404
    

    我发现了一些类似的问题,大多数都没有得到解答,或者有一些肮脏的解决方案,比如:

    总结:

    • 我不想用regex,因为将来会很痛苦的
    • 我也不能改变映射。我知道,这是最简单的方法,但我就是做不到。
    • 更改不起作用的订单
    • 创建专用控制器仍然存在一些路径问题

    我正在寻找一个简单的解决方案,完全自动化,最好从配置。问题是: 实现这一目标的正确方法是什么?

    3 回复  |  直到 6 年前
        1
  •  2
  •   DmqCsm    6 年前

    这个问题是由于Spring处理用户请求的方式造成的。有几个 HandlerMapping 它们按指定的顺序执行。对我们来说最重要的是这两个:

    1. RequestMappingHandlerMapping 注册地 WebMvcConfigurationSupport 具有 order =0(我们可以在源代码和文档中看到这一点)

      /**
       * Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping
       * requests to annotated controllers.
       */
      
    2. AbstractHandlerMapping 在中实例化 ResourceHandleRegistry 默认顺序 Integer.MAX_VALUE-1

      private int order = Ordered.LOWEST_PRECEDENCE - 1;
      

    使用路径创建请求映射时 /{profile}/{project} 并试图获得资源 /static/somefile.css ,您发送的请求被RequestMappingHandlerMapping捕获,但未到达由ResourceHandlerRegistry创建的HandlerMapping。

    解决这个问题的一个简单方法是 将订单设置为 -1 在里面 addResourceHandlers

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
            .addResourceLocations("classpath:/resources/static/");
    
        registry.setOrder(-1);
    }
    

    然后正确的HandlerMapping将为静态文件提供服务,如果没有这样的文件,它将把执行传递给您的控制器。

        2
  •  3
  •   alexbt    6 年前

    有点老套,但你可以在你的 profile 要排除静态的映射:

    为了 /{profile} :

    @RequestMapping("/{profile:^(?:static.+|(?!static).*)$}")
    

    为了 /{profile}/{project} :

    @RequestMapping("/{profile:^(?:static.+|(?!static).*)$}/{project}")
    

    编辑 :

    啊,我刚看到你已经找到了 正则表达式 作为一个可能的解决方案,我想知道(除其他解决方案外)这是否是正确的方法。

    就我个人而言,我更喜欢的解决方案是更改控制器的URI。我发现所有其他的解决方案都有点相似和老套:用控制器来处理静态文件,用regex来处理配置文件URI。。。

    如果无法更改URI,我可能会放弃使用上面的regex。我觉得很明显。

        3
  •  1
  •   dzikoysk Shan    6 年前

    我发现了基于自定义处理程序映射和伪控制器的有趣解决方案。 如果我们创造 AbstractHandlerMapping 豆子,如果春天和其他任何地方都不匹配 Controller :

    @Bean
    public AbstractHandlerMapping profileHandlerMapping(RequestMappingHandlerAdapter handlerAdapter) {
        HandlerMethodArgumentResolverComposite argumentResolvers = new HandlerMethodArgumentResolverComposite();
        argumentResolvers.addResolvers(handlerAdapter.getArgumentResolvers());
    
        ProfileController profileHandlerMapping = new ProfileController(argumentResolvers);
        profileHandlerMapping.setOrder(2);
    
        return profileHandlerMapping;
    }
    

    伪控制器:

    public class ProfileController extends AbstractHandlerMapping {
    
        private final HandlerMethodArgumentResolverComposite argumentResolvers;
    
        public ProfileController(HandlerMethodArgumentResolverComposite argumentResolvers) {
            this.argumentResolvers = argumentResolvers;
        }
    
        @Override
        protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
            InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(this, getClass().getMethod("profile", HttpServletRequest.class));
            handlerMethod.setHandlerMethodArgumentResolvers(argumentResolvers);
            return handlerMethod;
        }
    
        @ResponseBody
        public String profile(HttpServletRequest request) {
            return "Profile: " + request.getRequestURI();
        }
    
    }
    

    赞成的意见:

    • 与通配符冲突不存在
    • 易于将来维护,自动
    • 仅当没有匹配的控制器时才调度,也不会与404发生冲突

    欺骗:

    • 缺少对某些基于动态注释的变量的支持,例如 @PathVariable