Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ResourceHandler conflict with wildcards in Controllers

I'm trying to reserve all paths starting with /static/** for a resource handler. Unfortunately, I have some wildcards deriving out of the root path / in request mappings. Something like this:

Preview

What did I try?

  • ResourceHandlerRegistry#setOrder:

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/resources/static/");
    
        registry.setOrder(1);
    }
    
  • Various versions of interceptor (with or without order):

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

That is a half-hearted success (probably it won't even work if I change mapping to /{profile}/{project}/**), because:

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

I've found some similar questions, mostly unanswered or with a little dirty solutions, like:

  • Exclude manually static path in every controller with wildcard by regex
  • Change your mappings
  • Manage controllers order
  • Create dedicated controller pointed to /static/**
  • [...]

Summary:

  • I don't want to use regex, because it will be painful in the future
  • I also can't change mappings. I know, that it is the simplest way, but I'm just not able to do that.
  • Changing an order not working
  • Creating dedicated controller still have some issues with paths

I'm looking for a simple soulution, fully automated and preferably from the configuration. The question is: what's the proper way to achieve that?

like image 530
dzikoysk Avatar asked Aug 09 '18 13:08

dzikoysk


2 Answers

This issue is caused beause of the way that Spring handles requests from users. There are several HandlerMappings and they are executed in a specified order. Most important for us are these two:

  1. RequestMappingHandlerMapping registered in WebMvcConfigurationSupport with order=0 (we can see this in the source code and the documentation)

    /**
     * Return a {@link RequestMappingHandlerMapping} ordered at 0 for mapping
     * requests to annotated controllers.
     */
    
  2. AbstractHandlerMapping instantiated in ResourceHandleRegistry with default order Integer.MAX_VALUE-1

    private int order = Ordered.LOWEST_PRECEDENCE - 1;
    

When you create a RequestMapping with path /{profile}/{project} and try to reach resource /static/somefile.css, the request you send is grabbed by the RequestMappingHandlerMapping and does not reach the HandlerMapping created by ResourceHandlerRegistry.

A simple solution for this issue is to set order to -1 in addResourceHandlers

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

    registry.setOrder(-1);
}

Then the proper HandlerMapping will serve static files, and if there is no such file, it will pass execution to your Controllers.

like image 155
northpl93 Avatar answered Sep 24 '22 02:09

northpl93


Kinda hacky, but you could use a regex in your profile mapping to exclude static:

For /{profile}:

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

For /{profile}/{project}:

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

EDIT:

Ah, I just saw that you already found regex as a possible solution and were wondering (among other solutions) if that was the proper way to do it.

Personally, my preferred solution would be to change the URI for the Controller. I find all other solutions to be somewhat similar and hacky: fronting static with a controller, using regex for profile URI...

I think I'd fallback on using the regex above if changing the URI isn't possible. I find it explicit.

like image 37
alexbt Avatar answered Sep 21 '22 02:09

alexbt