Spring 3 RequestMapping: Pfadwert abrufen


133

Gibt es eine Möglichkeit, den vollständigen Pfadwert nach dem zu erhalten? requestMapping @PathVariable Werte analysiert wurden?

Das heißt: /{id}/{restOfTheUrl}sollte /1/dir1/dir2/file.htmlin id=1und analysieren könnenrestOfTheUrl=/dir1/dir2/file.html

Irgendwelche Ideen wären willkommen.

Antworten:


198

Ein nicht übereinstimmender Teil der URL wird als Anforderungsattribut mit dem Namen angezeigt 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);
    ...
}

63
Nein, das Attribut HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE enthält den GANZEN übereinstimmenden Pfad.
uthark

11
uthark ist richtig. Der Wert in restOfTheUrlwird der gesamte Pfad sein, nicht nur der verbleibende Teil, der von**
dcstraw

4
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE ist optional und kann für einige Implementierungen NULL oder "" sein. request.getRequestURI () gibt denselben Wert zurück und ist nicht optional.
Nidalpres

2
Diese Lösung funktioniert nicht mehr und ist unzuverlässig.
DolphinJava

51

Ich habe gerade das Problem gefunden, das meinem Problem entspricht. Mit HandlerMapping-Konstanten konnte ich ein kleines Dienstprogramm für diesen Zweck schreiben:

/**
 * 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;

}

19

Dies ist schon eine ganze Weile hier, aber ich poste dies. Könnte für jemanden nützlich sein.

@RequestMapping( "/{id}/**" )
public void foo( @PathVariable String id, HttpServletRequest request ) {
    String urlTail = new AntPathMatcher()
            .extractPathWithinPattern( "/{id}/**", request.getRequestURI() );
}

1
Das Problem mit diesem Code ist, dass er kein Servlet-Präfix und kein Mapping-Präfix verarbeitet.
Gavenkoa

11

Aufbauend auf Fabien Krubas bereits ausgezeichneter Antwort hielt ich es für schön, wenn der **Teil der URL über eine Annotation als Parameter für die Controller-Methode angegeben werden könnte, ähnlich wie @RequestParamund @PathVariablenicht immer mit einer Utility-Methode die ausdrücklich die HttpServletRequest. Hier ist ein Beispiel, wie dies implementiert werden könnte. Hoffentlich findet es jemand nützlich.

Erstellen Sie die Anmerkung zusammen mit dem Argumentauflöser:

@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));
        }

    }

}

Registrieren Sie den Methodenargument-Resolver:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

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

}

Verwenden Sie die Annotation in Ihren Controller-Handler-Methoden, um einfachen Zugriff auf den **Teil der URL zu erhalten:

@RestController
public class SomeController {

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

}

9

Sie müssen Folgendes verwenden 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)));

2
Bestätigung, dass dies mit der neuesten Version von Spring Boot funktioniert
Dave Bauman

1
Bestätigen Sie außerdem, dass diese Methode ab Spring Boot 2.2.4 RELEASE funktioniert.
tom_mai78101

5

Ich habe den Tuckey URLRewriteFilter verwendet, um Pfadelemente zu verarbeiten, die '/' Zeichen enthalten, da ich glaube, dass Spring 3 MVC sie noch nicht unterstützt.

http://www.tuckey.org/

Sie fügen diesen Filter in Ihre App ein und stellen eine XML-Konfigurationsdatei bereit. In dieser Datei stellen Sie Umschreiberegeln bereit, mit denen Sie Pfadelemente mit '/' Zeichen in Anforderungsparameter übersetzen können, mit denen Spring MVC mithilfe von @RequestParam ordnungsgemäß umgehen kann.

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>

Controller-Methode:

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

2

Ja, das restOfTheUrlgibt nicht nur den erforderlichen Wert zurück, sondern wir können den Wert mithilfe von ermittelnUriTemplate Matching ermitteln.

Ich habe das Problem gelöst, also hier die funktionierende Lösung für das Problem:

@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.*/
    }
}

1

Hier ist, wie ich es gemacht habe. Sie können sehen, wie ich die angeforderte URL in einen Dateisystempfad konvertiere (worum es in dieser SO-Frage geht). Bonus: und auch, wie man mit der Datei antwortet.

@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");
    }
}

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;
}

-4

Ich habe ein ähnliches Problem und habe es folgendermaßen gelöst:

@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
}

Beachten Sie, dass req.getPathInfo()der vollständige Pfad (mit {siteCode}und {fileName}.{fileExtension}) zurückgegeben wird, sodass Sie ihn bequem verarbeiten müssen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.