【问题标题】:Spring 3 RequestMapping: Get path valueSpring 3 RequestMapping:获取路径值
【发布时间】:2011-04-10 20:25:29
【问题描述】:

requestMapping@PathVariable的值被解析后有没有办法得到完整的路径值?

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

任何想法都将不胜感激。

【问题讨论】:

    标签: spring spring-mvc request-mapping


    【解决方案1】:

    URL 的不匹配部分被暴露为名为@9​​87654321@ 的请求属性:

    @RequestMapping("/{id}/**")
    public void foo(@PathVariable("id") int id, HttpServletRequest request) {
        String restOfTheUrl = (String) request.getAttribute(
            HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
        ...
    }
    

    【讨论】:

    • 否,属性 HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE 包含整个匹配路径。
    • uthark 是对的。 restOfTheUrl 中的值将是整个路径,而不仅仅是** 捕获的剩余部分
    • HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE 是可选的,对于某些实现可能为 NULL 或 ""。 request.getRequestURI() 返回相同的值,不是可选的。
    • 此解决方案不再有效且不可靠。
    【解决方案2】:

    我使用 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) {
      ...
    }
    

    【讨论】:

      【解决方案3】:

      刚刚发现与我的问题相对应的问题。使用 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;
      
      }
      

      【讨论】:

        【解决方案4】:

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

        @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}),因此您必须方便地处理。

        【讨论】:

        • 这个答案并不比接受的答案差 - 它还返回完整路径,请参阅:javaee.github.io/javaee-spec/javadocs/javax/servlet/http/… 返回与客户端在发出此请求时发送的 URL 关联的任何额外路径信息。额外的路径信息在 servlet 路径之后,但在查询字符串之前,并以“/”字符开头。
        【解决方案5】:
        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;
        }
        

        【讨论】:

          【解决方案6】:

          是的,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.*/
              }
          }
          

          【讨论】:

            【解决方案7】:

            这就是我的做法。您可以看到我如何将请求的URI 转换为文件系统路径(这个 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");
                }
            }
            

            【讨论】:

              【解决方案8】:

              这已经在这里很长一段时间了,但发布了这个。可能对某人有用。

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

              【讨论】:

              • 这段代码的问题是它不处理servlet前缀和映射前缀。
              【解决方案9】:

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

              【讨论】:

              • 确认这适用于最新版本的 Spring Boot
              • 还确认此方法从 Spring Boot 2.2.4 RELEASE 开始有效。
              【解决方案10】:

              Fabien Kruba's already excellent answer 的基础上,我认为如果URL 的** 部分可以通过注释作为参数提供给控制器方法会很好,其方式类似于@RequestParam 和@ 987654324@,而不是总是使用明确需要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...
                  }
              
              }
              

              【讨论】:

                【解决方案11】:

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

                【讨论】:

                  猜你喜欢
                  • 2016-10-20
                  • 2017-05-15
                  • 1970-01-01
                  • 2015-06-26
                  • 2018-01-03
                  • 1970-01-01
                  相关资源
                  最近更新 更多