Spring request mapping with regex like in javax.ws.rs

560 Views Asked by At

I'm trying rewrite this Google App Engine maven server repository to Spring.

I have problem with URL mapping. Maven repo server standard looks like this:

  1. URL with slash at the end, points to a folder, example:

    http://127.0.0.1/testDir/
    http://127.0.0.1/testDir/testDir2/
    
  2. all others (without slash at the end) point to files, example:

    http://127.0.0.1/testFile.jar
    http://127.0.0.1/testFile.jar.sha1
    http://127.0.0.1/testDir/testFile2.pom
    http://127.0.0.1/testDir/testFile2.pom.md5
    

Original app mapping for directories and for files.

There were used annotations @javax.ws.rs.Path which supports regexy differently than Spring.

I tried bunch of combinations, for example something like this:

@ResponseBody
@GetMapping("/{file: .*}")
public String test1(@PathVariable String file) {
    return "test1 " + file;
}

@ResponseBody
@GetMapping("{dir: .*[/]{1}$}")
public String test2(@PathVariable String dir) {
    return "test2 " + dir;
}

But I can't figure out how to do this in right way in Spring application.

I'd like to avoid writing a custom servlet dispatcher.

5

There are 5 best solutions below

0
On

Try this solution:

@GetMapping("**/{file:.+?\\..+}")
public String processFile(@PathVariable String file, HttpServletRequest request) {   
    return "test1 " + file;
}

@GetMapping("**/{dirName:\\w+}")
public String processDirectory(@PathVariable String dirName, HttpServletRequest request) {
    String dirPath = request.getRequestURI();
    return "test2 " + dirPath;
}

Results for URIs from the question:

test2 /testDir/
test2 /testDir/testDir2/

test1 testFile.jar
test1 testFile.jar.sha1
test1 testFile2.pom
test1 testFile2.pom.md5    
0
On

I had a similar problem once, also regarding a Spring implementation of a maven endpoint.

For the file endpoints, you could do something like this

/**
 * An example Maven endpoint for Jar files
 */
@GetMapping("/**/{artifactId}/{version}/{artifactId}-{version}.jar")
public ResponseEntity<String> getJar(@PathVariable("artifactId") String artifactId, @PathVariable("version") String version) {
   ...
}

This gives you the artifactId and the version, but for the groupId you would need to do some string parsing. You can get the current requestUri with the help of the ServletUriComponentsBuilder

String requestUri = ServletUriComponentsBuilder.fromCurrentRequestUri().build().toUri().toString();
// requestUri = /api/v1/com/my/groupId/an/artifact/v1/an-artifact-v1.jar

For the folder endpoints, I'm not sure if this will work, but you can give it a try

@GetMapping("/**/{artifactId}/{version}")
public ResponseEntity<String> getJar(@PathVariable("artifactId") String artifactId, @PathVariable("version") String version) {
   // groupId extracted as before from the requestUri
   ...
}
0
On

Don't know about your java code, but if you are verifying one path at a time, you can just check if the string ends in "/" for a folder and the ones that don't are files

\/{1}$

this regular expression just checks that the string ends with "/" if there is a match, you have a folder, if there is not, you have a file

0
On

Spring doesn't allow matching to span multiple path segments. Path segments are delimited values of path on path separator (/). So no regex combination will get you there. Spring 5 although allows the span multiple path segments only at the end of path using ** or {*foobar} to capture in foobar uri template variable for reactive stack but I don't think that will be useful for you.

Your options are limited. I think the best option if possible is to use different delimiter than / and you can use regex.

Other option ( which is messy ) to have catch all (**) endpoint and read the path from the request and determine if it is file or directory path and perform actions.

0
On

Well there is no other specific standard in Spring then the way you have used it. However if you can customize URL then I have a special way to differentiate directory and files. That will increase the scalibility and readability of application and will reduce lot of code for you.

Your Code as of now

@ResponseBody
@GetMapping("/{file: .*}")
public String test1(@PathVariable String file) {
    return "test1 " + file;
}

@ResponseBody
@GetMapping("{dir: .*[/]{1}$}")
public String test2(@PathVariable String dir) {
    return "test2 " + dir;
}

Change above code to as below in your controller class

private final Map<String, String> managedEntities=ImmutableMap.of(
        "file","Type_Of_Operation_You_want_For_File",
        "directory","Type_Of_Operation_You_want_For_Directory"
        );

@GetMapping(path = "/{type:file|directory}")
public String myFileOperationControl(@PathVariable String type){
        return "Test"+managedEntities.get(type));
        }

And proceed further the way you want to per your business logic. Let me know if you have any questions.

Note: Please simply enhance endpoint per your need.