Spring3RequestMapping: 获取路径值

是否有办法获得完整的路径值后,requestMapping @PathVariable值解析?

那就是: /{id}/{restOfTheUrl}应该能够将 /1/dir1/dir2/file.html解析为 id=1restOfTheUrl=/dir1/dir2/file.html

任何想法都可以。

202873 次浏览

URL 中未匹配的部分作为一个名为 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE的请求属性公开:

@RequestMapping("/{id}/**")
public void foo(@PathVariable("id") int id, HttpServletRequest request) {
String restOfTheUrl = new AntPathMatcher().extractPathWithinPattern(request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString(),request.getRequestURI());
...
}

我使用 Tuckey URLRewriteFilter 来处理包含’/’字符的路径元素,因为我认为 Spring 3 MVC 还不支持它们。

Http://www.tuckey.org/

将这个过滤器放入应用程序,并提供一个 XML 配置文件。在该文件中,您提供了重写规则,可以使用这些规则将包含“/”字符的路径元素转换为 Spring MVC 可以使用@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>

控制器方法:

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

刚发现这个问题和我的问题相对应。使用 HandlerMapping 常量,我能够为此编写一个小实用程序:

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


}

我也有类似的问题,我是这样解决的:

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

请注意,req.getPathInfo()将返回完整的路径(使用 {siteCode}{fileName}.{fileExtension}) ,因此您必须方便地处理。

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

是的,restOfTheUrl不仅返回所需的值,而且我们可以通过使用 UriTemplate匹配来获得这个值。

我已经解决了这个问题,所以这里有一个可行的解决方案:

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

我是这么做的。您可以看到我如何将 requestedURI 转换为文件系统路径(这个 SO 问题是关于什么的)。奖励: 还有如何使用文件进行响应。

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

这张照片放在这里有一段时间了,但是发布这张照片,可能对某些人有用。

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

你需要使用内置的 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)));

Fabien Kruba 的答案已经很棒了的基础上,我认为如果 URL 的 **部分可以通过注释作为控制器方法的一个参数给出会更好,这种方式类似于 @RequestParam@PathVariable,而不是总是使用明确需要 HttpServletRequest的实用方法。这里有一个例子可以说明如何实现这一点。希望有人觉得有用。

创建注释和参数解析器:

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


}


}

注册方法参数解析器:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {


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


}

使用控制器处理程序方法中的注释可以方便地访问 URL 的 **部分:

@RestController
public class SomeController {


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


}

改进@Daniel Jay Marcaida 的回答

@RequestMapping( "/{id}/**" )
public void foo( @PathVariable String id, HttpServletRequest request ) {
String restOfUrl = new AntPathMatcher()
.extractPathWithinPattern(
request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString(),
request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).toString());
}

或者

@RequestMapping( "/{id}/**" )
public void foo( @PathVariable String id, HttpServletRequest request ) {
String restOfUrl = new AntPathMatcher()
.extractPathWithinPattern(
request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString(),
request.getServletPath());
}