Determine route in multi route controller method - java

That defines multi route method in controller in Spring MVC
#RequestMapping(value={"/path", "/path2"}, method = RequestMethod.GET)
public String MyMethod () {
// Determine which route invoked the method
return null;
}
Is there a way to determine which route invoked the method?
Appreciate your kind help.

You could use HttpServletRequest which has a method called getRequestURL() to retrieve the actual URL, allowing you to parse which path was used.
However, another possibility is using path variables instead:
#RequestMapping(value = "/{path}", method = RequestMethod.GET)
public String myMethod(#PathVariable String path) {
// Do stuff with "path"
return null;
}
In this case, the path variable will contain whatever you enter matching the path given in your #RequestMapping, in your case it would be "path" or "path2". However, this will also allow other path variables as well ("path3" for example, ...), so you might want to validate it first before using.

I believe you can use HttpServletRequest:
#RequestMapping(value={"/path.html", "/path2.html"}, method = RequestMethod.GET)
public String MyMethod (HttpServletRequest request) {
// Determine which route invoked the method
String url = new String(request.getRequestURL());
log.debug("URL: " + url); //use whatever you use to log
return null;
}

Related

Spring RequestMapping multiple paths, identify which one is being called?

So I have a controller with #RequestMapping(value = { "/something", "/otherThing" })
I just can't seem to figure out how I can determine inside my controller as to which one of the paths are being served right now, "something" or the "otherThing".
Breaking down the controller and making 2 separate ones is not an option in my case.
Any ideas how I could go about determining the path being served inside a controller's method?
#RequestMapping(value = { "/something", "/otherThing" })
public void polymorpHandlerMethod(HttpServletRequest request) {
if (request.getContextPath().startsWith("/something")) {
// do stuff
} else if (request.getContextPath().startsWith("/otherThing")) {
// do more stuff
}
}
You can get the url using the below code and can get the path from it
URL url = new URL(request.getRequestURL().toString());
String path = url.getPath();
You can check this path and use it in your logic
Hope it will help your senario.

Correct way to put parameters in a function

I have a huge form with around 30 parameters and I don't think it's a good idea to do what I usually do.
The form will be serialized and pass all the parameters via ajax post to spring controller.
I usually do like this:
#RequestMapping(value = "/save-state", method = RequestMethod.POST)
public #ResponseBody
void deleteEnvironment(#RequestParam("environmentName") String environmentName, #RequestParam("imageTag") String imageTag) {
//code
}
but if I have 30 parameters I will have a huge parameter list in the function.
What is the usual and correct way to avoid this?
EDIT: What if I pass the HttpServlet request only?? The request will have all the parameters and I can simple call request.getParameters("").
There are two options I would suggest:
Take an HttpServletRequest object and fetch needed properties separately.
The problem is Spring's controllers are designed to eliminate such low-level API (Servlets API) calls. It's could be the right fit if a controller was too abstract (operates on abstract datasets), which means you wouldn't be able to define a DTO with a fixed-length number of parameters.
Construct a DTO class with the properties needed and take it as a parameter.
The advantage is you completely delegate low-level work to Spring and care only about your application logic.
You can do something like this:
#RequestMapping(value = "/save-state", method = RequestMethod.POST)
public void deleteEnvironment(#RequestBody MyData data) {
//code
}
Create a class containing all your form parameters and receive that on your method.
but if I have 30 parameters I will have a huge parameter list in the
function.
In your request, pass a JSON object that contains these information and create its counterpart in Java.
RequestMethod.POST is not design to perform deletion.
Usr rather RequestMethod.DELETE.
#RequestMapping(value = "/save-state", method = RequestMethod.DELETE)
public #ResponseBody
void deleteEnvironment(MyObject myObject) {
//code
}
Correct way is to serialize all parameters as Json and call back end api with one parameter.
On back-end side get that json and parse as objects.
Example:
` #RequestMapping(method = POST, path = "/task")
public Task postTasks(#RequestBody String json,
#RequestParam(value = "sessionId", defaultValue = "-1") Long sessionId)
throws IOException, AuthorizationException {
Task task = objectMapper.readValue(json, Task.class);
`

How to re-write request URI along with path variable in Spring?

I'm using Spring 4 right now and I have a controller defined a such:
#RequestMapping(value = "/{id}/definitions", method = RequestMethod.PUT)
#ResponseBody
public List<ResponseDTO> update(#PathVariable(value = "id") String id,
HttpServletRequest iRequest) throws Exception
{ ... }
I am expecting to receive an encrypted 'id' String coming as part of a path variable in the request itself. But what I need to do is re-write this request URI and decrypt it to another value (an integer, for example) and form another HTTP request with the original URI only with the transformed/decrypted value.
How can I get a hold of the entire URI and substitute the {id} with an integer?
For example, if the original request coming in looks like:
http://mycompany.com/my-service/kjAISOhalkjZjakmbbb/definitions
I want to transform everything after the context path:
/kjAISOhalkjZjakmbbb/definitions
to:
/123456/definitions
So finally, I can form a request to another service that might look like this:
http://mycompany.com/my-service-2/123456/definitions
Thank you!
Decrypt the value (however you do that).
Then just use a redirect or forward (probably a forward in your case):
#RequestMapping(value = "/{id}/definitions", method = RequestMethod.PUT)
public void update(#PathVariable(value = "id") String id,
HttpServletRequest iRequest) throws Exception {
String decryptedId = //decrypt here
//Do whatever else
//either forward: or redirect:
return "forward:my-service-2/" + decryptedId + "/definitions";
}

Spring: #RequestMapping with multiple values

I don't know this is the right approach but the easiest solution I can come up with in the moment.
I want to use multiple values in #RequestMapping and do the business logic in the method according which value is called the method. Example:
#RequestMapping(value = {"/delete", "/save"}, method = RequestMethod.POST)
public String crudOps(#ModelAttribute ("userForm") User user) {
// find user in repository....
if(value is delete) // don't know how to make this check
delete(user);
else
save(user);
}
How can I make that if statement work?
fter adding a comment above, I thought of a different solution by accessing the HttpServletRequest getServletPath method,
#RequestMapping(value = {"/delete", "/save"}, method = RequestMethod.POST)
public String crudOps(#ModelAttribute ("userForm") User user, HttpServletRequest request) {
// find user in repository....
if(request.getServletPath().equals("/delete"))
delete(user);
else
save(user);
}
You can use #PathVariale to grab part of the URL as follows
#RequestMapping(value = /{action}, method = RequestMethod.POST)
public String crudOps(#PathVariable String action, #ModelAttribute ("userForm") User user) {
// find user in repository....
if(action.equals("delete"))
delete(user);
else
save(user);}
I would personally go ahead with something like below,
#RequestMapping(value = "user/{userid}", method = RequestMethod.DELETE)
And use
#RequestMapping(value = "/save", method = RequestMethod.POST)
for creating an user. This helps in Single Responsibility Principle and more modular code. Let your method do one thing, and do it right!
Would recommend this link for using which HttpMethod for which purpose.

arguments for hasPermission in a generic controller

I am trying implement a generic controller class where each method has a structure similar to this:
#RequestMapping(value="cadastra")
#PreAuthorize("hasPermission(#user, 'cadastra_#this.class.name')")
public ModelAndView cadastra() throws InstantiationException, IllegalAccessException {
return new ModelAndView("privado/"+this.entity.getClass().getName()+"/cadastra", "command", this.entity.getClass().newInstance());
}
I am having trouble with the annotation PreAuthorize. the name for the permissionhave this structure: _. right now, I am getting a 403 Error when I try access the view mapped by the method. I also tried other variations like:
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.class.name)")
or
#PreAuthorize("hasPermission(#user, 'cadastra_#this.getClass().getName()')")
but with the same result. Anyone knows the right way to accomplish this?
UPDATE
I try call this function inside the methods from controller secured by this tag PreAuthorize:
private void expressionParser() {
System.out.println("expressionHandler()");
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("'cadastra_'+#this.class.name");
String message = (String) expression.getValue();
System.out.println("Message is " + message);
}
and when I run the application and open the view should be mapped by a method from controller, like this one:
#RequestMapping(value="cadastra")
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.class.name)")
public ModelAndView cadastra() throws InstantiationException, IllegalAccessException {
this.expressionParser();
return new ModelAndView("privado/"+this.entityClass.getName()+"/cadastra", "command", this.entityClass.newInstance());
}
No message is displayed on the console. So, I am thinking my application somehow aren't calling the methods from my generic controller. Am I right? If so, how I fix this?
My derived controllers follow this structure:
#Controller
#RequestMapping(value="usuario")
public class UsuarioController extends controller<Usuario> {
public UsuarioController() {
super(Usuario.class);
}
}
So you have difficulties with dynamic construction of permission name in the form of [methodName]_[classFullName] in SpEL expression.
See what SpEL documentation says about #this variable below
The variable #this is always defined and refers to the current evaluation object
(against which unqualified references are resolved).
Based on the documentation and a bit digging in the code the actual object the #this represents should be in your case an instance of org.springframework.security.access.expression.method.MethodSecurityExpressionRoot class. The class contains several helpful methods among others also getThis() method that returns the target object on which the secured method (a method annotated with #PreAuthorize) is being invoked.
Armed with this knowledge it should not be a big deal to construct the expression you require. In case of method named "cadastra", it should be as follows.
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.class.name)")
Hopefully it will also work correctly for secured methods inherited from a base class.
So, I solved this issue with this approach:
1) Adding a new method to my generic controller, where I return the name of the class:
public String getName() {
String expressao = entityClass.getName();
String nome_classe = new String();
StringTokenizer st = new StringTokenizer(expressao, ".");
while (st.hasMoreTokens()) {
nome_classe = st.nextToken();
}
return nome_classe;
}
2) Inside the annotation, I use the returned value by this method and concatenate the result with the constant string (using the notation described by the user #pgjecek in this topic):
#PreAuthorize("hasPermission(#user, 'cadastra_'+#this.this.name)")
and now it1s working perfectly.

Categories