Pemetaan Permintaan 3 Musim Semi: Dapatkan nilai jalur

133

Apakah ada cara untuk mendapatkan nilai path lengkap setelah requestMapping @PathVariablenilai diuraikan?

Yaitu: /{id}/{restOfTheUrl}harus mampu mengurai /1/dir1/dir2/file.htmlke id=1danrestOfTheUrl=/dir1/dir2/file.html

Setiap ide akan dihargai.

Monyet Musim Semi
sumber

Jawaban:

198

Bagian URL yang tidak cocok ditampilkan sebagai atribut permintaan bernama 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);
    ...
}
axtavt
sumber
63
Tidak, atribut HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE berisi jalur WHOLE yang cocok.
uthark
11
uthark benar. Nilai dalam restOfTheUrlakan menjadi seluruh jalur, bukan hanya bagian yang tersisa yang ditangkap oleh**
dcstraw
4
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE adalah opsional dan mungkin NULL atau "" untuk beberapa implementasi. request.getRequestURI () mengembalikan nilai yang sama dan bukan opsional.
nidalpres
2
Solusi ini tidak berfungsi lagi dan tidak dapat diandalkan.
DolphinJava
51

Baru saja menemukan masalah yang sesuai dengan masalah saya. Menggunakan konstanta HandlerMapping saya dapat menulis sebuah utilitas kecil untuk tujuan itu:

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

}
Fabien Kruba
sumber
19

Ini sudah di sini cukup lama tetapi memposting ini. Mungkin bermanfaat bagi seseorang.

@RequestMapping( "/{id}/**" )
public void foo( @PathVariable String id, HttpServletRequest request ) {
    String urlTail = new AntPathMatcher()
            .extractPathWithinPattern( "/{id}/**", request.getRequestURI() );
}
Daniel Jay Marcaida
sumber
1
Masalah dengan kode ini adalah bahwa ia tidak menangani awalan servlet dan pemetaan awalan.
gavenkoa
11

Membangun berdasarkan jawaban Fabien Kruba yang sudah sangat bagus , saya pikir akan lebih baik jika **bagian dari URL dapat diberikan sebagai parameter untuk metode pengontrol melalui anotasi, dengan cara yang mirip dengan @RequestParamdan @PathVariable, daripada selalu menggunakan metode utilitas yang secara eksplisit mengharuskan HttpServletRequest. Jadi, inilah contoh bagaimana hal itu diterapkan. Semoga ada yang merasa bermanfaat.

Buat anotasi, bersama dengan penyelesai argumen:

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

    }

}

Daftarkan pemecah argumen metode:

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

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

}

Gunakan anotasi dalam metode pengendali pengendali Anda untuk memiliki akses mudah ke **bagian URL:

@RestController
public class SomeController {

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

}
thejonwithnoh
sumber
9

Anda perlu menggunakan built-in 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)));
Suram
sumber
2
Mengkonfirmasi ini bekerja dengan versi terbaru dari Spring Boot
Dave Bauman
1
Juga mengonfirmasi bahwa metode ini berfungsi pada Spring Boot 2.2.4 RELEASE.
tom_mai78101
5

Saya telah menggunakan Tuckey URLRewriteFilter untuk menangani elemen path yang berisi karakter '/', karena saya belum berpikir Spring 3 MVC mendukungnya.

http://www.tuckey.org/

Anda memasukkan filter ini ke aplikasi Anda, dan memberikan file konfigurasi XML. Dalam file itu Anda memberikan aturan penulisan ulang, yang bisa Anda gunakan untuk menerjemahkan elemen path yang mengandung karakter '/' ke dalam parameter permintaan yang dapat ditangani oleh Spring MVC dengan benar menggunakan @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>

Metode pengontrol:

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

Ya restOfTheUrlitu tidak mengembalikan hanya nilai yang diperlukan tetapi kita bisa mendapatkan nilai dengan menggunakan UriTemplatepencocokan.

Saya telah memecahkan masalah, jadi inilah solusi untuk masalah ini:

@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.*/
    }
}
Anil Kumar Pandey
sumber
1

Inilah cara saya melakukannya. Anda dapat melihat bagaimana saya mengonversiURURI yang diminta ke jalur sistem file (tentang apa pertanyaan SO ini). Bonus: dan juga bagaimana menanggapi dengan file tersebut.

@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");
    }
}
neoneye
sumber
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;
}
xsampedro
sumber
-4

Saya memiliki masalah serupa dan saya menyelesaikannya dengan cara ini:

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

Perhatikan bahwa req.getPathInfo()akan mengembalikan jalur lengkap (dengan {siteCode}dan {fileName}.{fileExtension}) sehingga Anda harus memproses dengan mudah.

Nico
sumber