Languages
[Edit]
EN

Spring Boot 2 - request mapping with not decoded PathVariable (/path/{pathVariable})

4 points
Created by:
maxsior322
347

In this short article, we would like to show how to get not decoded @PathVariable value from URL in Spring Boot 2 MVC request.

Note: as not decoded @PathVariable value we understand: all characters that were escaped/encoded in URI component/segment will be not decoded, e.g. %20 will be not decoded as space (' '), etc.

Simple steps:

1. Create ExampleController.java file:

package example.controllers;

import example.annotations.PathSegment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.function.Function;

@Controller
public class ExampleController {

    @RequestMapping(
            method = RequestMethod.GET,
            value = "/items/{itemName}"
    )
    public ResponseEntity<String> getItem(
            @PathSegment(name = "itemName") String itemName  // <--- contains value that is not decoded (e.g. it means %20 is not decoded to space), etc.
    ) {
        // Some logic here ...
    }
}

2. Create PathSegment.java file: 

package example.annotations;

import org.springframework.core.MethodParameter;
import org.springframework.util.PathMatcher;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.*;
import java.util.Map;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface PathSegment {

    String name();

    class Resolver implements HandlerMethodArgumentResolver {

        private PathMatcher pathMatcher;

        public Resolver(PathMatcher pathMatcher) {
            this.pathMatcher = pathMatcher;
        }

        @Override
        public boolean supportsParameter(MethodParameter methodParameter) {
            Annotation annotation = methodParameter.getParameterAnnotation(PathSegment.class);
            return annotation != null;
        }

        @Override
        public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modeContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
            PathSegment pathSegment = methodParameter.getParameterAnnotation(PathSegment.class);
            if (pathSegment == null) {
                return null;
            }
            HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
            if (servletRequest == null) {
                return null;
            }
            String patternAttribute = (String) servletRequest.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
            String mappingAttribute = (String) servletRequest.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
            Map<String, String> templateVariables = this.pathMatcher.extractUriTemplateVariables(patternAttribute, mappingAttribute);
            if (templateVariables.isEmpty()) {
                return null;
            }
            return templateVariables.get(pathSegment.name());
        }
    }
}

3. MVCConfig.java file:

package example.config;

import example.annotations.PathSegment;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.PathMatcher;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class MVCConfig implements WebMvcConfigurer {

    @Autowired
    private PathMatcher pathMatcher;  // or replace it with bean creation function

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new PathSegment.Resolver(this.pathMatcher));
    }
}

See also

  1. Spring Boot 2 - request mapping with wildcard parameter (/path/**) 
Donate to Dirask
Our content is created by volunteers - like Wikipedia. If you think, the things we do are good, donate us. Thanks!
Join to our subscribers to be up to date with content, news and offers.
Native Advertising
🚀
Get your tech brand or product in front of software developers.
For more information Contact us
Dirask - we help you to
solve coding problems.
Ask question.

❤️💻 🙂

Join