Spring 3 RequestMapping: รับค่าพา ธ


140

มีวิธีรับค่าเส้นทางที่สมบูรณ์หลังจากrequestMapping @PathVariableแยกวิเคราะห์ค่าแล้วหรือไม่

นั่นคือ: /{id}/{restOfTheUrl}ควรจะสามารถที่จะแยก/1/dir1/dir2/file.htmlออกเป็นid=1และrestOfTheUrl=/dir1/dir2/file.html

ความคิดใด ๆ ที่จะได้รับการชื่นชม

คำตอบ:


202

ส่วนที่ไม่ตรงกันของ URL จะแสดงเป็นแอตทริบิวต์คำขอชื่อ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);
    ...
}

65
ไม่แอตทริบิวต์ HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE มีเส้นทางที่ตรงกันทั้งหมด
uthark

11
uthark พูดถูก ค่าในrestOfTheUrlจะเป็นเส้นทางทั้งหมดไม่ใช่แค่ส่วนที่เหลือที่จับโดย**
dcstraw

4
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE เป็นทางเลือกและอาจเป็น NULL หรือ "" สำหรับการใช้งานบางอย่าง request.getRequestURI () ส่งคืนค่าเดียวกันและไม่ใช่ทางเลือก
nidalpres

2
โซลูชันนี้ใช้ไม่ได้อีกต่อไปและไม่น่าเชื่อถือ
DolphinJava

มีการอัปเดตเกี่ยวกับสิ่งนี้สำหรับรุ่นใหม่ของฤดูใบไม้ผลิหรือไม่
LuisComS

52

พบปัญหาที่ตรงกับปัญหาของฉัน การใช้ค่าคงที่ 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;

}

21

นี่เป็นมาระยะหนึ่งแล้ว แต่โพสต์สิ่งนี้ อาจมีประโยชน์สำหรับใครบางคน

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

2
ปัญหาเกี่ยวกับรหัสนี้คือไม่สามารถจัดการคำนำหน้า servlet และคำนำหน้าการแมป
gavenkoa

12

จากคำตอบที่ยอดเยี่ยมของ Fabien ครูบาฉันคิดว่ามันจะดีถ้า**ส่วนของ 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...
    }

}

10

คุณต้องใช้ในตัว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
การยืนยันว่าใช้งานได้กับ Spring Boot เวอร์ชันล่าสุด
Dave Bauman

1
นอกจากนี้ยังยืนยันว่าวิธีนี้ใช้ได้ผลกับ Spring Boot 2.2.4 RELEASE
tom_mai78101

5

ฉันใช้ Tuckey URLRewriteFilter เพื่อจัดการองค์ประกอบพา ธ ที่มีอักขระ "/" แล้วเนื่องจากฉันไม่คิดว่า Spring 3 MVC รองรับพวกเขา

http://www.tuckey.org/

คุณใส่ตัวกรองนี้ในแอปของคุณและระบุไฟล์กำหนดค่า XML ในไฟล์นั้นคุณระบุกฎการเขียนซ้ำซึ่งคุณสามารถใช้เพื่อแปลองค์ประกอบเส้นทางที่มีอักขระ "/" เป็นพารามิเตอร์คำขอที่ Spring MVC สามารถจัดการได้อย่างเหมาะสมโดยใช้ @RequestParam

เว็บ INF / web.xml:

<filter>
  <filter-name>UrlRewriteFilter</filter-name>
  <filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
</filter>
<!-- map to /* -->

เว็บ 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) {
  ...
}

2

ใช่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.*/
    }
}

1

นี่คือวิธีที่ฉันทำ คุณสามารถดูวิธีที่ฉันแปลง 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");
    }
}

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

-3

ฉันมีปัญหาที่คล้ายกันและฉันแก้ไขด้วยวิธีนี้:

@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 แต่นำหน้าสตริงการสืบค้นและจะเริ่มต้นด้วยอักขระ "/"
Leon Roy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.