RequestMapping Spring 3: obtenir la valeur du chemin

133

Existe-t-il un moyen d'obtenir la valeur du chemin complet après que les requestMapping @PathVariablevaleurs ont été analysées?

C'est: /{id}/{restOfTheUrl}devrait être en mesure d'analyser /1/dir1/dir2/file.htmldans id=1etrestOfTheUrl=/dir1/dir2/file.html

Toute idée serait appréciée.

Singe de printemps
la source

Réponses:

198

La partie non correspondante de l'URL est exposée en tant qu'attribut de requête nommé HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE:

@RequestMapping("/{id}/**")
public void foo(@PathVariable("id") int id, HttpServletRequest request) {
    String restOfTheUrl = (String) request.getAttribute(
        HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
    ...
}
axtavt
la source
63
Non, l'attribut HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE contient le chemin correspondant ENTIER.
uthark
11
uthark a raison. La valeur dans restOfTheUrlsera le chemin complet, pas seulement la partie restante capturée par**
dcstraw
4
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE est facultatif et peut être NULL ou "" pour certaines implémentations. request.getRequestURI () renvoie la même valeur et n'est pas facultative.
nidalpres
2
Cette solution ne fonctionne plus et n'est pas fiable.
DolphinJava
51

Je viens de trouver ce problème correspondant à mon problème. En utilisant les constantes HandlerMapping, j'ai pu écrire un petit utilitaire à cet effet:

/**
 * Extract path from a controller mapping. /controllerUrl/** => return matched **
 * @param request incoming request.
 * @return extracted path
 */
public static String extractPathFromPattern(final HttpServletRequest request){


    String path = (String) request.getAttribute(
            HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
    String bestMatchPattern = (String ) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);

    AntPathMatcher apm = new AntPathMatcher();
    String finalPath = apm.extractPathWithinPattern(bestMatchPattern, path);

    return finalPath;

}
Fabien Kruba
la source
19

Cela a été ici un certain temps, mais en affichant cela. Cela pourrait être utile pour quelqu'un.

@RequestMapping( "/{id}/**" )
public void foo( @PathVariable String id, HttpServletRequest request ) {
    String urlTail = new AntPathMatcher()
            .extractPathWithinPattern( "/{id}/**", request.getRequestURI() );
}
Daniel Jay Marcaida
la source
1
Le problème avec ce code est qu'il ne gère pas le préfixe de servlet et le préfixe de mappage.
gavenkoa
11

En me basant sur la réponse déjà excellente de Fabien Kruba , j'ai pensé que ce serait bien si la **partie de l'URL pouvait être donnée comme paramètre à la méthode du contrôleur via une annotation, d'une manière similaire à @RequestParamet @PathVariable, plutôt que d'utiliser toujours une méthode utilitaire qui exigeait explicitement le HttpServletRequest. Voici donc un exemple de la façon dont cela pourrait être mis en œuvre. Espérons que quelqu'un le trouve utile.

Créez l'annotation, ainsi que le résolveur d'arguments:

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

    class Resolver implements HandlerMethodArgumentResolver {

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

        @Override
        public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
            HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
            return request == null ? null : new AntPathMatcher().extractPathWithinPattern(
                    (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE),
                    (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE));
        }

    }

}

Enregistrez le résolveur d'arguments de méthode:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

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

}

Utilisez l'annotation dans vos méthodes de gestionnaire de contrôleur pour accéder facilement à la **partie de l'URL:

@RestController
public class SomeController {

    @GetMapping("/**")
    public void someHandlerMethod(@WildcardParam String wildcardParam) {
        // use wildcardParam here...
    }

}
thejonwithnoh
la source
9

Vous devez utiliser intégré pathMatcher:

@RequestMapping("/{id}/**")
public void test(HttpServletRequest request, @PathVariable long id) throws Exception {
    ResourceUrlProvider urlProvider = (ResourceUrlProvider) request
            .getAttribute(ResourceUrlProvider.class.getCanonicalName());
    String restOfUrl = urlProvider.getPathMatcher().extractPathWithinPattern(
            String.valueOf(request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)),
            String.valueOf(request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)));
Sinistre
la source
2
Confirmer que cela fonctionne avec la dernière version de Spring Boot
Dave Bauman
1
Confirmant également que cette méthode fonctionne à partir de Spring Boot 2.2.4 RELEASE.
tom_mai78101
5

J'ai utilisé l'URLRewriteFilter de Tuckey pour gérer les éléments de chemin contenant des caractères '/', car je ne pense pas que Spring 3 MVC les supporte encore.

http://www.tuckey.org/

Vous placez ce filtre dans votre application et fournissez un fichier de configuration XML. Dans ce fichier, vous fournissez des règles de réécriture, que vous pouvez utiliser pour traduire les éléments de chemin contenant les caractères '/' en paramètres de requête que Spring MVC peut traiter correctement en utilisant @RequestParam.

WEB-INF / web.xml:

<filter>
  <filter-name>UrlRewriteFilter</filter-name>
  <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<!-- map to /* -->

WEB-INF / urlrewrite.xml:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite
    PUBLIC "-//tuckey.org//DTD UrlRewrite 3.0//EN"
    "http://tuckey.org/res/dtds/urlrewrite3.0.dtd">
<urlrewrite>
  <rule>
    <from>^/(.*)/(.*)$</from>
    <to last="true">/$1?restOfTheUrl=$2</to>
</urlrewrite>

Méthode de contrôleur:

@RequestMapping("/{id}")
public void handler(@PathVariable("id") int id, @RequestParam("restOfTheUrl") String pathToFile) {
  ...
}
sdouglass
la source
2

Oui, le restOfTheUrlne renvoie pas uniquement la valeur requise mais nous pouvons obtenir la valeur en utilisant la UriTemplatecorrespondance.

J'ai résolu le problème, alors voici la solution de travail pour le problème:

@RequestMapping("/{id}/**")
public void foo(@PathVariable("id") int id, HttpServletRequest request) {
String restOfTheUrl = (String) request.getAttribute(
    HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
    /*We can use UriTemplate to map the restOfTheUrl*/
    UriTemplate template = new UriTemplate("/{id}/{value}");        
    boolean isTemplateMatched = template.matches(restOfTheUrl);
    if(isTemplateMatched) {
        Map<String, String> matchTemplate = new HashMap<String, String>();
        matchTemplate = template.match(restOfTheUrl);
        String value = matchTemplate.get("value");
       /*variable `value` will contain the required detail.*/
    }
}
Anil Kumar Pandey
la source
1

Voici comment je l'ai fait. Vous pouvez voir comment je convertis l'URI demandée en chemin de système de fichiers (sur quoi porte cette question SO). Bonus: et aussi comment répondre avec le fichier.

@RequestMapping(value = "/file/{userId}/**", method = RequestMethod.GET)
public void serveFile(@PathVariable("userId") long userId, HttpServletRequest request, HttpServletResponse response) {
    assert request != null;
    assert response != null;

    // requestURL:  http://192.168.1.3:8080/file/54/documents/tutorial.pdf
    // requestURI:  /file/54/documents/tutorial.pdf
    // servletPath: /file/54/documents/tutorial.pdf
    // logger.debug("requestURL: " + request.getRequestURL());
    // logger.debug("requestURI: " + request.getRequestURI());
    // logger.debug("servletPath: " + request.getServletPath());

    String requestURI = request.getRequestURI();
    String relativePath = requestURI.replaceFirst("^/file/", "");

    Path path = Paths.get("/user_files").resolve(relativePath);
    try {
        InputStream is = new FileInputStream(path.toFile());  
        org.apache.commons.io.IOUtils.copy(is, response.getOutputStream());
        response.flushBuffer();
    } catch (IOException ex) {
        logger.error("Error writing file to output stream. Path: '" + path + "', requestURI: '" + requestURI + "'");
        throw new RuntimeException("IOError writing file to output stream");
    }
}
neoneye
la source
0
private final static String MAPPING = "/foo/*";

@RequestMapping(value = MAPPING, method = RequestMethod.GET)
public @ResponseBody void foo(HttpServletRequest request, HttpServletResponse response) {
    final String mapping = getMapping("foo").replace("*", ""); 
    final String path = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
    final String restOfPath = url.replace(mapping, "");
    System.out.println(restOfPath);
}

private String getMapping(String methodName) {
    Method methods[] = this.getClass().getMethods();
    for (int i = 0; i < methods.length; i++) {
        if (methods[i].getName() == methodName) {
            String mapping[] = methods[i].getAnnotation(RequestMapping.class).value();
            if (mapping.length > 0) {
                return mapping[mapping.length - 1];
            }
        }
    }
    return null;
}
xsampedro
la source
-4

J'ai un problème similaire et je l'ai résolu de cette manière:

@RequestMapping(value = "{siteCode}/**/{fileName}.{fileExtension}")
public HttpEntity<byte[]> getResource(@PathVariable String siteCode,
        @PathVariable String fileName, @PathVariable String fileExtension,
        HttpServletRequest req, HttpServletResponse response ) throws IOException {
    String fullPath = req.getPathInfo();
    // Calling http://localhost:8080/SiteXX/images/argentine/flag.jpg
    // fullPath conentent: /SiteXX/images/argentine/flag.jpg
}

Notez que req.getPathInfo()retournera le chemin complet (avec {siteCode}et {fileName}.{fileExtension}), vous devrez donc traiter facilement.

Nico
la source