Czy istnieje sposób na uzyskanie pełnej wartości ścieżki po requestMapping
@PathVariable
przeanalizowaniu wartości?
Czyli:
/{id}/{restOfTheUrl}
powinien być w stanie przetworzyć /1/dir1/dir2/file.html
do id=1
irestOfTheUrl=/dir1/dir2/file.html
Wszelkie pomysły będą mile widziane.
spring
spring-mvc
request-mapping
Spring Monkey
źródło
źródło
restOfTheUrl
będzie całą ścieżką, a nie tylko pozostałą częścią przechwyconą przez**
Właśnie znalazłem ten problem odpowiadający mojemu problemowi. Używając stałych HandlerMapping udało mi się napisać małe narzędzie do tego celu:
/** * 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; }
źródło
To było już od dłuższego czasu, ale publikuję to. Może się komuś przyda.
@RequestMapping( "/{id}/**" ) public void foo( @PathVariable String id, HttpServletRequest request ) { String urlTail = new AntPathMatcher() .extractPathWithinPattern( "/{id}/**", request.getRequestURI() ); }
źródło
Opierając się na już doskonałej odpowiedzi Fabiena Kruby , pomyślałem, że byłoby miło, gdyby
**
część adresu URL mogła być podana jako parametr do metody kontrolera za pośrednictwem adnotacji, w sposób podobny do metody narzędziowej@RequestParam
i@PathVariable
, zamiast zawsze przy użyciu metody narzędziowej który wyraźnie wymagał rozszerzeniaHttpServletRequest
. Oto przykład, jak można to zaimplementować. Mam nadzieję, że ktoś uzna to za przydatne.Utwórz adnotację wraz z funkcją rozpoznawania argumentów:
@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)); } } }
Zarejestruj program rozpoznawania argumentów metody:
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(new WildcardParam.Resolver()); } }
Użyj adnotacji w metodach obsługi kontrolera, aby mieć łatwy dostęp do
**
części adresu URL:@RestController public class SomeController { @GetMapping("/**") public void someHandlerMethod(@WildcardParam String wildcardParam) { // use wildcardParam here... } }
źródło
Musisz użyć wbudowanego
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)));
źródło
Użyłem Tuckey URLRewriteFilter do obsługi elementów ścieżki, które zawierają znaki „/”, ponieważ nie sądzę, aby Spring 3 MVC je jeszcze obsługiwał.
http://www.tuckey.org/
Umieszczasz ten filtr w swojej aplikacji i udostępniasz plik konfiguracyjny XML. W tym pliku podajesz reguły przepisywania, których możesz użyć do przetłumaczenia elementów ścieżki zawierających znaki '/' na parametry żądania, z którymi Spring MVC może poprawnie sobie poradzić za pomocą @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>
Metoda kontrolera:
@RequestMapping("/{id}") public void handler(@PathVariable("id") int id, @RequestParam("restOfTheUrl") String pathToFile) { ... }
źródło
Tak,
restOfTheUrl
nie zwraca tylko wymaganej wartości, ale możemy uzyskać wartość za pomocąUriTemplate
dopasowania.Rozwiązałem problem, więc tutaj działające rozwiązanie problemu:
@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.*/ } }
źródło
Oto jak to zrobiłem. Możesz zobaczyć, jak konwertuję żądany identyfikatorURI na ścieżkę systemu plików (o co chodzi w tym pytaniu SO). Bonus: a także sposób odpowiedzi z plikiem.
@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"); } }
źródło
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; }
źródło
Mam podobny problem i rozwiązałem w ten sposób:
@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 }
Zauważ, że
req.getPathInfo()
zwróci pełną ścieżkę (z{siteCode}
a{fileName}.{fileExtension}
), więc trzeba będzie przetwarzać wygodnie.źródło