How to exclude url mappings from #RequestMapping in Spring? - java

I've a request mapping that handles any string after the context e.g. www.example.com/anystring
I'm handling it as follows:
#RequestMapping(value="/{str}", method = RequestMethod.GET)
public String getApp(#PathVariable("str") String anyString, ModelMap model) {
//Do something
}
The problem is I've 2-3 URLs in my app where the URL is as follows: www.example.com/about, www.example.com/contact etc.
I wrote Request Mappings for them as follows:
#RequestMapping("/about")
public String getAboutPage() {
return "about";
}
But obviously, since I've already declared that any string should be handled by the getApp(), the getAboutPage() never gets executed.
How can I exclude /about, /contact etc from getApp() mapping.
We can obviously add another keyword to the URL string, but that's not possible in my app use case.
Kindly help. :(
EDIT:
Should I just handle /about, /contact inside getApp() like:
#RequestMapping(value="/{str}", method = RequestMethod.GET)
public String getApp(#PathVariable("str") String anyString, ModelMap model) {
if(anyString.equals("about")){
//do about related stuff
}
if(anyString.equals("contact")){
//do contact related stuff
}
//Do something
}
Is there a better way?

Specifying the HTTP request method in the "catch-all" mapping is probably making the path matcher consider it to be more specific than the absolute path mappings.
Specify the request method on the absolute paths, and the mapping comparator should order the absolute matches before the one containing the path variable.
eg.
#RequestMapping("/about", method = RequestMethod.GET)
Alternatively, you could remove the method specification on the catch-all:
#RequestMapping("/{str}")
It is entirely dependent upon your url structure and whether or not any of those paths will accept different http request methods.

Related

Java Spring Controller handling a ridiculous url

Okay so I am using a payment service called Thrive cart, I am doing this for a membership website I'm creating. When the user has paid I want them to be redirected to a URL where I can use that data to update the current users information.
The data that get's sent in the params is insane:
http://localhost:5000/user/welcome?thrivecart%5Baccount_id%5D=3196&thrivecart%5Baccount_name%5D=testacount&thrivecart%5Bcustomer%5D%5Bemail%5D=testname8%40gmail.com&thrivecart%5Bcustomer%5D%5Baddress%5D%5Bcountry%5D=GB&thrivecart%5Bcustomer%5D%5Baddress%5D%5Bzip%5D=pe303wu&thrivecart%5Border%5D%5B0%5D%5Bt%5D=product&thrivecart%5Border%5D%5B0%5D%5Bid%5D=6&thrivecart%5Border%5D%5B0%5D%5Bn%5D=Monthly+membership&thrivecart%5Border%5D%5B0%5D%5Bp%5D=799&thrivecart%5Border%5D%5B0%5D%5Bq%5D=1&thrivecart%5Border%5D%5B0%5D%5Bpo%5D=60120&thrivecart%5Border%5D%5B1%5D%5Bt%5D=product&thrivecart%5Border%5D%5B1%5D%5Bid%5D=6&thrivecart%5Border%5D%5B1%5D%5Bn%5D=Monthly+membership&thrivecart%5Border%5D%5B1%5D%5Bp%5D=799&thrivecart%5Border%5D%5B1%5D%5Bq%5D=1&thrivecart%5Border%5D%5B1%5D%5Bpo%5D=60120&thrivecart%5Border_currency%5D=GBP&thrivecart%5Border_id%5D=1041278&thrivecart%5Border_tax%5D=0.2&thrivecart%5Border_tax_id%5D=gb&thrivecart%5Border_total%5D=799&thrivecart%5Bpayment_processor%5D=paypal&thrivecart%5Bproduct_id%5D=6&thrivecart%5Bpurchases%5D%5B0%5D=6&thrivecart%5Bpurchases%5D%5B1%5D=6&thrivecart_hash=a5b711d2288b4cb587511811bc0a3473
So far I've set up a simple controller which doesn't get hit:
#RestController
#RequestMapping("/user")
public class UserController {
#RequestMapping(value = "/welcome", method = RequestMethod.POST)
public void welcomeMember(#PathVariable String data) {
System.out.println(data);
}
}
How do I deal with crazy data like this? Do I have to specific each path param?
First of all, what you seem to get are not path elements but request parameters, so you will need #RequestParam annotations to get the values.
Since there are so many request parameters, I would also recommend to take just one parameter, a Map<String, String>. That Map will contain all the parameters as key/value pairs, for example:
key: "thrivecart[account_id]"
value: "3196"
If you're not sure whether you receive a POST or a GET request, you can also add a second parameter to receive the HttpMethod.
Change your RestController to:
#RestController
#RequestMapping("/user")
public class UserController {
#RequestMapping(value = "/welcome")
public void welcomeMember(#RequestParam Map<String, String> data, HttpMethod method) {
System.out.println(method);
System.out.println(data);
}
}
That looks like a problem with how the rest api is called from the service consumer side.
try sending the data in a request body rather then as a param. This way you can use a POJO to handle the data.
Question 1: So far I've set up a simple controller which doesn't get hit:
As per your URL http://localhost:5000/user/welcome "user" seems to be your projects context name. Try removing #RequestMapping("/user") from your class.
Also, instead of#PathVariable String data use #RequestParam Map<String,String> params. #PathVariable String data is used when data is part of url but in your case it's parameter. Final code should be like below.
#RestController
public class UserController {
#RequestMapping(value = "/welcome", method = RequestMethod.POST)
public void welcomeMember(#RequestParam Map<String,String> params ) {
for(Map.Entry<String, String> entry : params.entrySet()){
//This will print all paramters name and their value
System.out.println(entry.getKey() + "-" + entry.getValue());
}
}
}
Question 2: How do I deal with crazy data like this? Do I have to specific each path param?
I will suggest to follow standard practice. Send data in json format. There are different ways for this depend upon front end technology you are using. One way is Link

How to dynamically specify Spring Restful WebService URL?

I want to dynamically enter my spring restful url, how to do this?
first of all, before i trying to create dynamical url, i create the static one. Here is what i do :
#RequestMapping(value = "/insert/{id}/{name}/{address}", method = RequestMethod.GET,headers="Accept=application/json")
public void insertsoheaderdinamis(#PathVariable String id, #PathVariable String name, #PathVariable String address) throws ParseException {
}
above is my static url code. in the future, what i need is, i need a new pathvariable like this localhost:8080/SpringServiceJsonSample/service/updatepool/insert/{here goes id}/{here goes name}/{here goes address}/{new variable goes phone number}/{here goes age}.
i don't want to change my code, so i decided to create a dynamic url. While i read around internet.
i trying to do this :
#RequestMapping(value = "/insert/{path}/**", method = RequestMethod.GET,headers="Accept=application/json")
public void insertdynamicurl(#PathVariable("path") String path, HttpServletRequest request) throws ParseException {
}
but this won't do, even i can't get into my function while debuging it. It always give me "noHandlerFound" in my console log. How to do the dynamically url for springrestful service properly?
You can have a look at URI Template Patterns with Regular Expressions which suggests on using regular expressions in #RequestMapping annotations.
The #RequestMapping annotation supports the use of regular expressions in URI template variables. The syntax is {varName:regex} where the first part defines the variable name and the second - the regular expression. For example:
#RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\d\.\d\.\d}.{extension:\.[a-z]}")
public void handle(#PathVariable String version, #PathVariable String extension) {
// ...
}
}
In addition to this, you can match rest of the URL string using request attribute name HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, as shown below.
#RequestMapping("/{id}/**")
public void foo(#PathVariable("id") int id, HttpServletRequest request) {
String restOfTheUrl = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE);
...
}
Shishir's approach is good, but even the regular expression will match the value only up to a first forward slash, because the default AntPathMatcher uses a forward slash as a delimiter for path variables.
This means that you'll always have to statically list the mappings with all the possible path variable combinations. However, on the side of function arguments you don't have to list all the path variables, as you can use a map that will catch all the path variables regardless of the count, and this you can use to achieve a level of generality, something like
#RequestMapping(value = {"/insert/{id}/{name}/{address}", "/insert/{id}/{name}/{address}/{phoneNumber}", "/insert/{id}/{name}/{address}/{phoneNumber}/{age}"} , method = RequestMethod.GET,headers="Accept=application/json")
public void insertsoheaderdinamis(
#PathVariable Map<String, String> pathVariables) {
// to access the values of your path variables do something like
if (pathVariables.containsKey("id")) {
String id= pathVariables.get("id");
}
// do your stuff
}

Spring MVC #Path variable with { } braces

Am developing an application using spring boot. In REST controller i prefer to use path variable(#PathVariabale annotation). My code fetching the path variable but it contatins { } braces as it is in the url. Please any one suggest me to solve this issue
#RequestMapping(value = "/user/item/{loginName}", method = RequestMethod.GET)
public void getSourceDetails(#PathVariable String loginName) {
try {
System.out.println(loginName);
// it print like this {john}
} catch (Exception e) {
LOG.error(e);
}
}
URL
http://localhost:8080/user/item/{john}
Out put in controller
{john}
Use http://localhost:8080/user/item/john to submit your request instead.
You give Spring a value of "{john}" to the path variable loginName, so Spring get it with the "{}"
Web MVC framework states that
URI Template Patterns
URI templates can be used for convenient access to selected parts of a
URL in a #RequestMapping method.
A URI Template is a URI-like string, containing one or more variable
names. When you substitute values for these variables, the template
becomes a URI. The proposed RFC for URI Templates defines how a URI is
parameterized. For example, the URI Template
http://www.example.com/users/{userId} contains the variable userId.
Assigning the value fred to the variable yields
http://www.example.com/users/fred.
In Spring MVC you can use the #PathVariable annotation on a method
argument to bind it to the value of a URI template variable:
#RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(#PathVariable String ownerId, Model model) {
Owner owner = ownerService.findOwner(ownerId);
model.addAttribute("owner", owner);
return "displayOwner";
}
The URI Template " /owners/{ownerId}" specifies the variable name
ownerId. When the controller handles this request, the value of
ownerId is set to the value found in the appropriate part of the URI.
For example, when a request comes in for /owners/fred, the value of
ownerId is fred.

Get requested value(URL) when using #RequestMapping annotations

When I map multiple values to #RequestMapping(like Multiple Spring #RequestMapping annotations), can I get the requested value(URL)?
Like this:
#RequestMapping(value={"/center", "/left"}, method=RequestMethod.GET)
public String getCenter(Model model) throws Exception {
String requestedValue = getRequestedValue(); // I want this.
// I want to do something like this with requested value.
String result;
if (requestedValue.equals("center")
result = "center";
else if (requestedValue.equals("left")
result = "left";
return result;
}
You can have the Request (HttpServletRequest) itself as an parameter of the handler method. So you can then inspect the request url to get the "value".
#RequestMapping(value={"/center", "/left"}, method=RequestMethod.GET)
public String getCenter(Model model, HttpServletRequest request) throws Exception {
String whatYouCallValue = request.getServletPath();
....
Javadoc: https://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpServletRequest.html#getServletPath--
Btw: if I understand you right, you want to have different urls, not different values.
From Spring 3.1.0, you can use URI Template Patterns with Regular Expressions.
#RequestMapping(value={"/{path:[a-z-]+}"}, method=RequestMethod.GET)
public String getCenter(#PathVariable String path) throws Exception {
// "path" is what I want
}
From Spring 3.1.0, you can use ServletUriComponentsBuilder
#RequestMapping(value={"/center", "/left"}, method=RequestMethod.GET)
public String getCenter(Model model) throws Exception {
UriComponentsBuilder builder = ServletUriComponentsBuilder.fromCurrentRequest();
String requestedValue = builder.buildAndExpand().getPath(); // I want this.
System.out.println(requestedValue);
// I want to do something like this with requested value.
String result="fail";
if (requestedValue.equals("center"))
result = "center";
else if (requestedValue.equals("left"))
result = "left";
return result;
}
Use RequestParam annotation. You can also add a parameter of type HttpServletRequest to your method and then getParameters from that.
Addition to the best answer #Hugh_Lee:
This method will work for all not mapped requests. If you want to use this method just for two (or several) cases only, e.g. "/center" and "/left", you may do following. Rename "center" to "positionCenter", "left" to "positionLeft" (or add another common word). So the code would be like this:
#RequestMapping(value={"/{path:position+[A-Za-z-]+}"}, method=RequestMethod.GET)
public String getCenter(#PathVariable String path) throws Exception {
// "path" is what I want
}
Following regex will make your method to be executed only for the urls /center and /left. And you can get the value with #PathVariable annotation.
#GetMapping("/{path:^center$|^left$}")
public ResponseEntity<?> whatIsThePath(#PathVariable String path){
// path is either "center" or "left"
}

Spring Web MVC - validate individual request params

I'm running a webapp in Spring Web MVC 3.0 and I have a number of controller methods whose signatures are roughly as follows:
#RequestMapping(value = "/{level1}/{level2}/foo", method = RequestMethod.POST)
public ModelAndView createFoo(#PathVariable long level1,
#PathVariable long level2,
#RequestParam("foo_name") String fooname,
#RequestParam(value = "description", required = false) String description);
I'd like to add some validation - for example, description should be limited to a certain length or fooname should only contain certain characters. If this validation fails, I want to return a message to the user rather than just throw some unchecked exception (which would happen anyway if I let the data percolate down to the DAO layer). I'm aware of JSR303 but have not worked with it and don't quite understand how to apply it in a Spring context.
From what I understand, another option would be to bind the #RequestBody to an entire domain object and add validation constraints there, but currently my code is set up to accept individual parameters as shown above.
What is the most straightforward way to apply validation to input parameters using this approach?
This seems to be possible now (tried with Spring 4.1.2), see https://raymondhlee.wordpress.com/2015/08/29/validating-spring-mvc-request-mapping-method-parameters/
Extract from above page:
Add MethodValidationPostProcessor to Spring #Configuration class:
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
Add #Validated to controller class
Use #Size just before #RequestParam
#RequestMapping("/hi")
public String sayHi(#Size(max = 10, message = "name should at most 10 characters long") #RequestParam("name") String name) {
return "Hi " + name;
}
Handle ConstraintViolationException in an #ExceptionHandler method
There's nothing built in to do that, not yet anyway. With the current release versions you will still need to use the WebDataBinder to bind your parameters onto an object if you want automagic validation. It's worth learning to do if you're using SpringMVC, even if it's not your first choice for this task.
It looks something like this:
public ModelAndView createFoo(#PathVariable long level1,
#PathVariable long level2,
#Valid #ModelAttribute() FooWrapper fooWrapper,
BindingResult errors) {
if (errors.hasErrors() {
//handle errors, can just return if using Spring form:error tags.
}
}
public static class FooWrapper {
#NotNull
#Size(max=32)
private String fooName;
private String description;
//getset
}
If you have Hibernate Validator 4 or later on your classpath and use the default dispatcher setup it should "Just work."
Editing since the comments were getting kind of large:
Any Object that's in your method signature that's not one of the 'expected' ones Spring knows how to inject, such as HttpRequest, ModelMap, etc, will get data bound. This is accomplished for simple cases just by matching the request param names against bean property names and calling setters. The #ModelAttribute there is just a personal style thing, in this case it isn't doing anything. The JSR-303 integration with the #Valid on a method parameter wires in through the WebDataBinder. If you use #RequestBody, you're using an object marshaller based on the content type spring determines for the request body (usually just from the http header.) The dispatcher servlet (AnnotationMethodHandlerAdapter really) doesn't have a way to 'flip the validation switch' for any arbitrary marshaller. It just passes the web request content along to the message converter and gets back a Object. No BindingResult object is generated, so there's nowhere to set the Errors anyway.
You can still just inject your validator into the controller and run it on the object you get, it just doesn't have the magic integration with the #Valid on the request parameter populating the BindingResult for you.
If you have multiple request parameters that need to be validated (with Http GET or POST). You might as well create a custom model class and use #Valid along with #ModelAttribute to validate the parameters. This way you can use Hibernate Validator or javax.validator api to validate the params. It goes something like this:
Request Method:
#RequestMapping(value="/doSomething", method=RequestMethod.GET)
public Model dosomething(#Valid #ModelAttribute ModelRequest modelRequest, BindingResult result, Model model) {
if (result.hasErrors()) {
throw new SomeException("invalid request params");
}
//to access the request params
modelRequest.getFirstParam();
modelRequest.getSecondParam();
...
}
ModelRequest class:
class ModelRequest {
#NotNull
private String firstParam;
#Size(min = 1, max = 10, message = "You messed up!")
private String secondParam;
//Setters and getters
public void setFirstParam (String firstParam) {
this.firstParam = firstParam;
}
public String getFirstParam() {
return firstParam;
}
...
}
Hope that helps.

Categories

Resources