Bản đồ yêu cầu mùa xuân 3: Nhận giá trị đường dẫn


133

Có cách nào để có được giá trị đường dẫn đầy đủ sau khi các requestMapping @PathVariablegiá trị được phân tích cú pháp không?

Đó là: /{id}/{restOfTheUrl}sẽ có thể phân tích /1/dir1/dir2/file.htmlthành id=1restOfTheUrl=/dir1/dir2/file.html

Có những câu chuyện mới trên trang chủ.

Câu trả lời:


198

Phần không khớp của URL được hiển thị dưới dạng một thuộc tính yêu cầu có tên 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
Không, thuộc tính HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE chứa đường dẫn khớp WHOLE.
uthark

11
uthark là đúng. Giá trị trong restOfTheUrlsẽ là toàn bộ đường dẫn, không chỉ phần còn lại được chụp bởi**
dcstraw

4
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE là tùy chọn và có thể là NULL hoặc "" cho một số triển khai. request.getRequestURI () trả về cùng một giá trị và không phải là tùy chọn.
nidalpres

2
Giải pháp này không hoạt động nữa và không đáng tin cậy.
Cá heo

51

Chỉ cần tìm thấy vấn đề tương ứng với vấn đề của tôi. Sử dụng các hằng số HandlerMapping tôi có thể viết một tiện ích nhỏ cho mục đích đó:

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

Điều này đã ở đây khá lâu nhưng đăng bài này. Có thể hữu ích cho một ai đó.

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

1
Vấn đề với mã này là nó không xử lý tiền tố servlet và tiền tố ánh xạ.
gavenkoa

11

Dựa trên câu trả lời đã rất xuất sắc của Fabien Kruba , tôi nghĩ sẽ thật tuyệt nếu **phần URL có thể được cung cấp dưới dạng tham số cho phương thức điều khiển thông qua chú thích, theo cách tương tự @RequestParam@PathVariable, thay vì luôn sử dụng phương thức tiện ích mà rõ ràng yêu cầu HttpServletRequest. Vì vậy, đây là một ví dụ về cách điều đó có thể được thực hiện. Hy vọng ai đó thấy nó hữu ích.

Tạo chú thích, cùng với trình giải quyết đối số:

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

    }

}

Đăng ký trình giải quyết đối số phương thức:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

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

}

Sử dụng chú thích trong các phương thức xử lý bộ điều khiển của bạn để có quyền truy cập dễ dàng vào **phần URL:

@RestController
public class SomeController {

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

}

9

Bạn cần sử dụng tích hợp sẵn 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
Xác nhận điều này hoạt động với phiên bản mới nhất của Spring Boot
Dave Bauman

1
Đồng thời xác nhận rằng phương thức này hoạt động kể từ Spring Boot 2.2.4 ĐÁNG TIN CẬY.
tom_mai78101

5

Tôi đã sử dụng Tuckey URLRewriteFilter để xử lý các phần tử đường dẫn có chứa các ký tự '/', vì tôi không nghĩ Spring 3 MVC hỗ trợ chúng.

http://www.tuckey.org/

Bạn đặt bộ lọc này vào ứng dụng của mình và cung cấp tệp cấu hình XML. Trong tệp đó, bạn cung cấp các quy tắc viết lại, mà bạn có thể sử dụng để dịch các phần tử đường dẫn chứa các ký tự '/' thành các tham số yêu cầu mà Spring MVC có thể xử lý đúng bằng cách sử dụng @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>

Phương pháp điều khiển:

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

2

Có, restOfTheUrlkhông chỉ trả về giá trị bắt buộc nhưng chúng ta có thể nhận được giá trị bằng cách sử dụng UriTemplatekhớp.

Tôi đã giải quyết vấn đề, vì vậy đây là giải pháp làm việc cho vấn đề:

@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

Đây là cách tôi đã làm nó. Bạn có thể thấy cách tôi chuyển đổi requestURI thành đường dẫn hệ thống tập tin (câu hỏi SO này là gì). Tiền thưởng: và cả cách trả lời với tập tin.

@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

Tôi có một vấn đề tương tự và tôi đã giải quyết theo cách này:

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

Lưu ý rằng req.getPathInfo()sẽ trả về đường dẫn đầy đủ (có {siteCode}{fileName}.{fileExtension}) vì vậy bạn sẽ phải xử lý thuận tiện.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.