Search code examples
springencodingutf-8getcharacter-encoding

UTF-8 decoding in Spring Boot GET request to static resource


I have this @Bean redirecting requests to my Spring Boot backend.

@Bean
WebMvcConfigurer configurer () {
    return new WebMvcConfigurerAdapter() {
        @Override
        public void addResourceHandlers (ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/data/static/images/**")
                    .addResourceLocations("file:" + System.getProperty("user.dir") + "/static/img/");
        }
    };
}

It works perfectly for URLs such as:

http://localhost:4200/data/static/images/champion/tiles/Ahri_0.jpg

But not for URLs such as:

http://localhost:4200/data/static/images/champion/tiles/Tahm%20Kench_0.jpg

The image is correctly shown in my Angular2 front end if the champion name does not contain any of: space, ampersand or single quote characters.

I ran a trace level logging debug and made both types of requests -- one with and without a "bad" character. Currently, it seems as if the backend searches for the correct file. However, it turns out that it claims it can't find it. I quintuple checked the file in my insanity, I know it is there and that the path printed is correct.

Here is an example log message:

2018-11-18 05:07:14.496 TRACE 9897 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Testing handler map [org.springframework.web.servlet.handler.SimpleUrlHandlerMapping@4d0bdef0] in DispatcherServlet with name 'dispatcherServlet'
2018-11-18 05:07:14.497 DEBUG 9897 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping  : Matching patterns for request [/data/static/images/champion/tiles/Tahm Kench_0.jpg] are [/data/static/images/**, /**]
2018-11-18 05:07:14.497 DEBUG 9897 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping  : URI Template variables for request [/data/static/images/champion/tiles/Tahm Kench_0.jpg] are {}
2018-11-18 05:07:14.497 DEBUG 9897 --- [nio-8080-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapping [/data/static/images/champion/tiles/Tahm Kench_0.jpg] to HandlerExecutionChain with handler [ResourceHttpRequestHandler [locations=[URL [file:/home/nuradin/Development/Java/riot-api-interface/static/img/]], resolvers=[org.springframework.web.servlet.resource.PathResourceResolver@5b5b59]]] and 1 interceptor
2018-11-18 05:07:14.497 TRACE 9897 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Testing handler adapter [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter@132f4851]
2018-11-18 05:07:14.497 TRACE 9897 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@5b8d72dc]
2018-11-18 05:07:14.498 DEBUG 9897 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/data/static/images/champion/tiles/Tahm%20Kench_0.jpg] is: -1
2018-11-18 05:07:14.498 DEBUG 9897 --- [on(7)-127.0.0.1] sun.rmi.transport.tcp                    : RMI TCP Connection(7)-127.0.0.1: (port 34127) op = 82
2018-11-18 05:07:14.498 TRACE 9897 --- [nio-8080-exec-1] o.s.w.s.resource.PathResourceResolver    : Resolving resource for request path "champion/tiles/Tahm Kench_0.jpg"
2018-11-18 05:07:14.498 TRACE 9897 --- [nio-8080-exec-1] o.s.w.s.resource.PathResourceResolver    : Checking location: URL [file:/home/nuradin/Development/Java/riot-api-interface/static/img/]

EDIT: I've implemented the class in the accepted answer, and it's definitely working because I printed the resource name (after encoding the characters I had trouble with.) However, the result is still a 404 error.

Log is below. The champion/Aatrox2Epng bit is due to a println statement in the method @slimane posted below.

2018-11-18 05:56:40.509 TRACE 12951 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Testing handler adapter [org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter@465ac973]
2018-11-18 05:56:40.509 TRACE 12951 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Testing handler adapter [org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter@37df7ae5]
2018-11-18 05:56:40.509 DEBUG 12951 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Last-Modified value for [/data/static/images/champion/Aatrox.png] is: -1
2018-11-18 05:56:40.509 TRACE 12951 --- [nio-8080-exec-1] o.s.w.s.r.CachingResourceResolver        : Resolving resource for request path "champion/Aatrox.png"
2018-11-18 05:56:40.510 TRACE 12951 --- [nio-8080-exec-1] o.e.r.c.EncodedPathResourceResolver      : Resolving resource for request path "champion/Aatrox.png"
2018-11-18 05:56:40.510 TRACE 12951 --- [nio-8080-exec-1] o.e.r.c.EncodedPathResourceResolver      : Checking location: URL [file:/home/nuradin/Development/Java/riot-api-interface/static/8.23.1/img]
champion/Aatrox2Epng
2018-11-18 05:56:40.511 TRACE 12951 --- [nio-8080-exec-1] o.e.r.c.EncodedPathResourceResolver      : No match for location: URL [file:/home/nuradin/Development/Java/riot-api-interface/static/8.23.1/img]
2018-11-18 05:56:40.511 TRACE 12951 --- [nio-8080-exec-1] o.s.w.s.r.ResourceHttpRequestHandler     : No matching resource found - returning 404
2018-11-18 05:56:40.511 DEBUG 12951 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling

another EDIT -- sorry for the confusion, I changed the path because I thought paths with periods would work now.

@Bean
WebMvcConfigurer configurer () {
    return new WebMvcConfigurerAdapter() {
        @Override
        public void addResourceHandlers (ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/data/static/images/**")
                    .addResourceLocations("file:" + System.getProperty("user.dir") + "/static/8.23.1/img")
                    .resourceChain(true)
                    .addResolver(encodedPathResourceResolver());
        }
    };
}

Solution

  • define your own PathResourceResolver as below:

    import org.springframework.core.io.Resource;
    import org.springframework.web.servlet.resource.PathResourceResolver;
    import org.springframework.web.servlet.resource.ResourceResolver;
    import java.io.IOException;
    
    public class CustomPathResourceResolver extends PathResourceResolver implements ResourceResolver {
    
        @Override
        protected Resource getResource(String resourcePath, Resource location) throws IOException {
            //fixes problems with whitespaces in url
            resourcePath = resourcePath.replace(" ","%20");
            return super.getResource(resourcePath, location);
        }
    }
    

    and then register it in your configuration:

    @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
    
            registry.addResourceHandler("/data/static/images/**")
                    .addResourceLocations("file:" + System.getProperty("user.dir") + "/static/img/")
                    .resourceChain(true)
                    .addResolver(new CustomPathResourceResolver())
                    ;
        }