27

I have to get params from URL using @PathValiable in SpringBoot application. These params often have slashes. I don't have a control about what a user would enter in URL so I would like to get what he has entered and then I can handle with it.

I have already looked through materials and answers here, I don't think that for me the good solution is to ask users somehow encode the entering params.

The SpringBoot code is simple:

@RequestMapping("/modules/{moduleName}")
@ResponseBody
public String moduleStrings (@PathVariable("moduleName") String moduleName) throws Exception {

  ...

}

So the URL for example would be the following:

http://localhost:3000/modules/...

The issue is that the param moduleName often has slashes. For example,

metadata-api\cb-metadata-services OR
app-customization-service-impl\\modules\\expand-link-schemes\\common\\app-customization-service-api

So a user definetely can enter:

http://localhost:3000/modules/metadata-api\cb-metadata-services

Is this possible to get everything what a user has entered in URL after /modules/?

If anyone tell me what are the good ways to handle such issue.

Kirill Ch
  • 5,496
  • 4
  • 44
  • 65

4 Answers4

33

Basing on P.J.Meisch's answer I have come to the simple solution for my case. Also it allows to take into account several slashes in the URL param. It doesn't allow to work with backslashes as in the previous answer too.

@RequestMapping(value = "/modules/**", method = RequestMethod.GET)
@ResponseBody
public String moduleStrings(HttpServletRequest request) {

    String requestURL = request.getRequestURL().toString();

    String moduleName = requestURL.split("/modules/")[1];

    return "module name is: " + moduleName;

}
Kirill Ch
  • 5,496
  • 4
  • 44
  • 65
25

This code gets the complete path:

@RequestMapping(value = "/modules/{moduleBaseName}/**", method = RequestMethod.GET)
@ResponseBody
public String moduleStrings(@PathVariable String moduleBaseName, HttpServletRequest request) {
    final String path =
            request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).toString();
    final String bestMatchingPattern =
            request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE).toString();

    String arguments = new AntPathMatcher().extractPathWithinPattern(bestMatchingPattern, path);

    String moduleName;
    if (null != arguments && !arguments.isEmpty()) {
        moduleName = moduleBaseName + '/' + arguments;
    } else {
        moduleName = moduleBaseName;
    }

    return "module name is: " + moduleName;
}
Kirill Ch
  • 5,496
  • 4
  • 44
  • 65
P.J.Meisch
  • 18,013
  • 6
  • 50
  • 66
  • I can call _localhost:8080/modules/abc_ which returns "module name is: abc" and _localhost:8080/modules/abc/def_ which returns "module name is: abc/def". Do you envode the backslash in the url? – P.J.Meisch Apr 18 '17 at 13:24
  • It really goes into the method. That is good. However, for some reason it goes in circle (reaches return statement MANY TIMES and moduleName changes every time). – Kirill Ch Apr 18 '17 at 13:30
  • Great! Just add @ResponseBody. That is really works! – Kirill Ch Apr 18 '17 at 13:32
  • 2
    ah, that's the difference; I had my controller annotated with `@RestController` instead of `@Controller`, so I had the `@ResponseBody` already per default. – P.J.Meisch Apr 18 '17 at 13:35
  • 1
    `AntPathMatcher` should be cached, and maybe can be replaced with spring's ant path matcher. – martian May 05 '19 at 02:34
3
import org.springframework.web.util.UriUtils;

@RequestMapping(value = "/modules/**", method = RequestMethod.GET)
@ResponseBody
public String moduleStrings(HttpServletRequest request) {
    final String url = request.getRequestURL().toString();
    final String modules = url.split("/modules")[1];
    final String safeModules = UriUtils.decode(modules, StandardCharsets.UTF_8);
        
    return "replaces %20 with space";
}
-4

Is this a school assignment or something? Why would you want to put free text in the path? Instead, why not use a parameter variable?

I am not sure it is a great idea to have the user entering complicated urls that include free text. For one, it is hard for users to remember complicated urls, so it isn't user friendly. Second, they can do some weird stuff.

Instead have a main page they go to that might have links (or something like that) to take them to further page. If they need to enter free text, give them a text box to add data to, a button to click that will make something like an ajax call containing the data. The path would have everything except the free text. You can do that like this...

@RequestMapping(value = "/modules/{moduleBaseName}", method = RequestMethod.GET)
@ResponseBody
public String moduleStrings(@PathVariable String moduleBaseName,
                            @RequestParam(value = "moduleName", required = false) String moduleName 
                            HttpServletRequest request) 
{
    return "module name is: " + moduleName;
}

Then on the client side you would just need to set the data attribute in your ajax call to a json that contains the "moduleName" attribute. Something like this.

var params = {};
params.moduleName = userEnteredText;
jQuery.ajax({
    type: "GET",
    url: 'http://localhost:3000/modules'.json'),
    dataType: "json",
    data: params,
    async: true,
    success: function(data) { callback(data); },
    error: function() { alert("Error!!!"); }
});
ajpieri
  • 306
  • 3
  • 11