Small question regarding how to get the Spring request mapping (GET mapping, POST mapping...), the path (route) parameter(s) as variable(s). A bit of background, we currently have a use case where we take a route, compute some information, (the Disaster Recovery URL, the test URL, the most available URL, but the question is not here) and respond back. This is just a partial example to have something a bit more concrete in the question.
Query at http://the-main-host.com/firstRoute
We return http://go-to-region-us-west3.com/firstRoute and maybe minutes later, we return http://go-to-region-eu-central.com/firstRoute , but again, this is not the question.
#GetMapping(path = "/{id}/firstRoute", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<String> question(#PathVariable #NotBlank final String id, String url) {
String theUrlEnrichedWithOtherInformation = computeExtraInformationForURL(id, url);
return Mono.just(theUrlEnrichedWithOtherInformation + id + "/firstRoute");
}
With time, we are now at some 300 of those handlers, all with real use cases
#PostMapping(path = "/{id}/twoHundredAndFiveRoute", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<String> question(#PathVariable #NotBlank final String id, String url) {
String theUrlEnrichedWithOtherInformation = computeExtraInformationForURL(id, url);
return Mono.just(theUrlEnrichedWithOtherInformation + id + "/twoHundredAndFiveRoute");
}
#GetMapping(path = "/{id}/twoHundredSixtySevenRoute", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<String> question(#PathVariable #NotBlank final String id, String url) {
String theUrlEnrichedWithOtherInformation = computeExtraInformationForURL(id, url);
return Mono.just(theUrlEnrichedWithOtherInformation + id + "/twoHundredSixtySevenRoute");
}
We managed to make it more maintainable by using the Spring array of the path annotation. This way of doing is a bit nicer, it brought down our two hundred methods down to a single digit. We would like to keep using this way is possible. However, we lose the information what was the path being invoked.
#GetMapping(path = {"/{id}/firstRoute", "/{id}/twoHundredSixtySevenRoute"}, produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<String> question(#PathVariable #NotBlank final String id, String url) {
String theUrlEnrichedWithOtherInformation = computeExtraInformationForURL(id, url);
return Mono.just(theUrlEnrichedWithOtherInformation + id + getThePathThatWasUsedFromTheArrayPlease());
}
Is it possible to get it back, like in this speculative example?
What you need is to access the HttpServletRequest instance. This has a lot of information about http invocation.
Spring allow us an easy way to access to this instance:
#RequestMapping(value = "/report/{objectId}", method = RequestMethod.GET)
public #ResponseBody void generateReport(
#PathVariable("objectId") Long objectId,
HttpServletRequest request) {
}
If you have access to HttpServletRequest, you can get any part of the url:
Example: http://myhost:8080/people?lastname=Fox&age=30
String uri = request.getScheme() + "://" + // "http" + "://
request.getServerName() + // "myhost"
":" + // ":"
request.getServerPort() + // "8080"
request.getRequestURI() + // "/people"
"?" + // "?"
request.getQueryString(); // "lastname=Fox&age=30"
request.getRequestURI() should have the path value that you need.
You can get it from HttpServletRequest - request.getRequestURI(). Either autowire it or use it from method parameter.
Related
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.
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;
}
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;
}
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";
}
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!!!"); }
});
In Java-Jersey, it is possible to receive a dynamic path to a resource, e.g.
localhost:8080/webservice/this/is/my/dynamic/path
#GET
#Path("{dynamicpath : .+}")
#Produces(MediaType.APPLICATION_JSON)
public String get(#PathParam("dynamicpath") String p_dynamicpath) {
return p_dynamicpath;
}
prints out: this/is/my/dynamic/path
Question: how to do this in Spring MVC?
For multiple items inside your path you can access the dynamic path values like this:
#RequestMapping(value="/**", method = RequestMethod.GET)
public String get(HttpServletRequest request) throws Exception {
String dynPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
System.out.println("Dynamic Path: " + dynPath );
return dynPath;
}
If you know beforehand hoe many levels of path variables you'll have you can code them explicit like
#RequestMapping(value="/{path1}/{path2}/**", method = RequestMethod.GET)
public String get(#PathVariable("path1") String path1,
#PathVariable("path2") String path2,
HttpServletRequest request) throws Exception {
String dynPath = (String) request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
System.out.println("Dynamic Path: " + dynPath );
return dynPath;
}
If you want to see the String returned in your browser, you need to declare the method #ResponseBody as well (so the String you return is the content of your response):
#RequestMapping(value="/**", method = RequestMethod.GET, produces = "text/plain")
#ResponseBody
public String get(HttpServletRequest request) throws Exception {
Given a method like:
#RequestMapping(value = {"/foo"}, method = RequestMethod.GET)
public String getMappingValueInMethod() {
log.debug("requested "+foo); //how can I make this refer to /foo programmatically?
return "bar";
}
The use case is for refactoring some lengthly code. I have several GET methods doing roughly the same thing and only the request mapping value is different.
I've looked at using path variables, but this is not really what I want (unless there's some clever use of it that I don't see). I could also get a value from the HttpServletRequest like in this post, but not sure whether there's a better way.
Solution 1
With HttpServletRequest.
#RequestMapping(value = "/foo", method = RequestMethod.GET)
public String fooMethod(HttpServletRequest request) {
String path = request.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).toString();
System.out.println("path foo: " + path);
return "bar";
}
Solution 2
With reflection.
#RequestMapping(value = "/foo2", method = RequestMethod.GET)
public String fooMethod2() {
try {
Method m = YourClassController.class.getMethod("fooMethod2");
String path = m.getAnnotation(RequestMapping.class).value()[0];
System.out.println("foo2 path: " + path);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
return "bar";
}
If you want get path from class (instead method) you can use:
String path = YourClassController.class.getAnnotation(RequestMapping.class).value();
Solution 3
With #PathVariable.
#RequestMapping(value = {"/{foo3}"}, method = RequestMethod.GET)
public #ResponseBody String fooMethod3(#PathVariable("foo3") String path) {
path = "/" + path; // if you need "/"
System.out.println("foo3 path: " + path);
return "bar";
}
The simplest way of doing this would be putting the array directly in the request mapping i am assuming this is what you want.
#RequestMapping(value = {"/foo","/foo1","/foo2"}, method = RequestMethod.GET)
public String getMappingValueInMethod(HttpServletRequest request) {
log.debug("requested "+request.getRequestURI());
return request.getRequestURI();
}
Then name the jsp files similar to the uri or other wise you could store the mapping between the request uri and the name of the page in the db .
Hello i'm new in implementing Spring REST Web Service what's the cause of my error.
Here's my code
#RequestMapping(value = "/message/{regID}/name/{name}", method = RequestMethod.GET, headers = "Accept=application/json")
public String getMessage(#PathVariable String regID, #PathVariable String name) {
return "Hello Alvin!" + regID + " " + name;
}
i want to call it using the web browser but i failed to successfully invoke it but when i call single parameter of may another RequestMapping is Successfully completed.. Here is the RequestMapping that i successfully called
#RequestMapping(value = "/country/{id}", method = RequestMethod.GET, headers = "Accept=application/json")
public Country getCountryById(#PathVariable int id) {
List<Country> listOfCountries = new ArrayList<Country>();
listOfCountries = createCountryList();
for (Country country : listOfCountries) {
if (country.getId() == id)
return country;
}
return null;
}
Or How can i implement multiple parameter for my RequestMapping..?
The chances are that you're using the InternalResourceViewResolver, in which case the methods that return String will interpret the returned valued as a view name that will be searched inside the locations designated in the view resolver. Your no mapping found probably refers to that the framework can't find a view name that matches what you're returning
As your intention seems to be to return the text only you should simply additional map your method with #ResponseBody which will in turn add your text to response body instead of interpreting it as a view name, so
#ResponseBody
public String getMessage(#PathVariable String regID, #PathVariable String name)
Otherwise, your mapping is just fine
I you have more than one path variables, you need to sepecify the identifier in #PathVariable:
#RequestMapping(value = "/message/{regID}/name/{name}", method = RequestMethod.GET, headers = "Accept=application/json")
public String getMessage(#PathVariable("regID") String regID, #PathVariable("name") String name) {
return "Hello Alvin!" + regID + " " + name;
}
I am developing a project using Spring Boot. I've a controller which accepts GET requests.
Currently I'm accepting requests to the following kind of URLs:
http://localhost:8888/user/data/002
but I want to accept requests using query parameters:
http://localhost:8888/user?data=002
Here's the code of my controller:
#RequestMapping(value="/data/{itemid}", method = RequestMethod.GET)
public #ResponseBody
item getitem(#PathVariable("itemid") String itemid) {
item i = itemDao.findOne(itemid);
String itemname = i.getItemname();
String price = i.getPrice();
return i;
}
Use #RequestParam
#RequestMapping(value="user", method = RequestMethod.GET)
public #ResponseBody Item getItem(#RequestParam("data") String itemid){
Item i = itemDao.findOne(itemid);
String itemName = i.getItemName();
String price = i.getPrice();
return i;
}
While the accepted answer by afraisse is absolutely correct in terms of using #RequestParam, I would further suggest to use an Optional<> as you cannot always ensure the right parameter is used. Also, if you need an Integer or Long just use that data type to avoid casting types later on in the DAO.
#RequestMapping(value="/data", method = RequestMethod.GET)
public #ResponseBody
Item getItem(#RequestParam("itemid") Optional<Integer> itemid) {
if( itemid.isPresent()){
Item i = itemDao.findOne(itemid.get());
return i;
} else ....
}
To accept both #PathVariable and #RequestParam in the same /user endpoint:
#GetMapping(path = {"/user", "/user/{data}"})
public void user(#PathVariable(required=false,name="data") String data,
#RequestParam(required=false) Map<String,String> qparams) {
qparams.forEach((a,b) -> {
System.out.println(String.format("%s -> %s",a,b));
}
if (data != null) {
System.out.println(data);
}
}
Testing with curl:
curl 'http://localhost:8080/user/books'
curl 'http://localhost:8080/user?book=ofdreams&name=nietzsche'
In Spring boot: 2.1.6, you can use like below:
#GetMapping("/orders")
#ApiOperation(value = "retrieve orders", response = OrderResponse.class, responseContainer = "List")
public List<OrderResponse> getOrders(
#RequestParam(value = "creationDateTimeFrom", required = true) String creationDateTimeFrom,
#RequestParam(value = "creationDateTimeTo", required = true) String creationDateTimeTo,
#RequestParam(value = "location_id", required = true) String location_id) {
// TODO...
return response;
#ApiOperation is an annotation that comes from Swagger api, It is used for documenting the apis.
To accept both Path Variable and query Param in the same endpoint:
#RequestMapping(value = "/hello/{name}", method = RequestMethod.POST)
public String sayHi(
#PathVariable("name") String name,
#RequestBody Topic topic,
//#RequestParam(required = false, name = "s") String s,
#RequestParam Map<String, String> req) {
return "Hi "+name +" Topic : "+ topic+" RequestParams : "+req;
}
URL looks like : http://localhost:8080/hello/testUser?city=Pune&Pin=411058&state=Maha
I was interested in this as well and came across some examples on the Spring Boot site.
// get with query string parameters e.g. /system/resource?id="rtze1cd2"&person="sam smith"
// so below the first query parameter id is the variable and name is the variable
// id is shown below as a RequestParam
#GetMapping("/system/resource")
// this is for swagger docs
#ApiOperation(value = "Get the resource identified by id and person")
ResponseEntity<?> getSomeResourceWithParameters(#RequestParam String id, #RequestParam("person") String name) {
InterestingResource resource = getMyInterestingResourc(id, name);
logger.info("Request to get an id of "+id+" with a name of person: "+name);
return new ResponseEntity<Object>(resource, HttpStatus.OK);
}
See here also