Spring 4: Multiple base path in one controller - java

I have a question about Spring 4.
My controller is accessible from a URL, but I want to have a second URL where only the beginning is different to access the same endpoints of this controller.
Here is my controller:
#RestController("MyController")
#RequestMapping(value={"/abc/def/ghi","/ijk/def/ghi"})
public class MyController {
// code continuation
But I get this error message.
Multiple class level mappings defined on class com.MyController
Did I miss something?
Thanks.
Edit : as M. Dudek mentioned, it was indeed necessary to upgrade Hateoas.

#RequestMapping has a String[] value parameter (not values), so you should be able to specify multiple values like this:
#RequestMapping(value={"/abc/def/ghi"},{"/ijk/def/ghi"})

maybe this?
#RestController("MyController")
#RequestMapping(value={"/abc/def/ghi","/ijk/def/ghi"})
public class MyController {
// code continuation

Related

Spring boot - Controller catching all URLs

I am building a Spring boot web app and am using annotations for controller/url mapping.
I have several controllers annotated with #RequestMapping with the url value set (both empty strings and specific URLs) which are working fine e.g.
#Controller
#RequestMapping("/accounts")
class SignInController {
#Autowired PartyService partyService
#RequestMapping(value="", method = RequestMethod.GET )
public String signinPage( Model model) {
Navigating to /accounts renders the sign-in page correctly.
However, if I add a controller with no RequestMapping values e.g.
#Controller
class CustomController {
#RequestMapping
public String transform( Model model ) {
Then any URL I enter that doesn't match any other specific controller is getting handled by this controller (so pages I would expect to 404 all just renderthis page). Is this expected behaviour? I was not expecting this, and as the RequestMapping value defaults to empty and is an antMatcher I wouldn't have thought it would handle all other URLs.
The reason I have this controller with out RequestMapping defined is because I want to also have a SimpleUrlMappingHandler defined with some explicit URLs going to that controller, and if I don't include the #Controller & #RequestMapping annotations to that controller then I get an error about not being able to find the handler method (maybe the problem is that I have mis-understood the implementation details of that).
Should my custom controller be handling all URLs? If so, is there something I can do so it doesnt and only gets called for the explicit SimpleUrlMappingHandler I have defined?
As mentioned in the comments - I just removed the #Controller annotation from the class, and then explicitly defined the controller as a #Bean in my config class and explicitly assigned that to the mapping in the SimpleUrlMappingHandler configuration

Insert class in all spring model

I have on user registration form, a startup controler (with request method get) that loads the user class to that form and a post method to save the user. Everything uses spring. But now I want to move the registration form to a popup in the header file and every page will import that.
The problem is, I don't want to insert the user class in all Get methods. What I really want is to "inject" in all models the user class without having to do something in all other methods.
Is this possible to do? And how?
In Spring 3.2, there is a #ControllerAdvice class level annotation introduced. If you place your model attribute in a separate class with #ControllerAdvice, it will be available to all controllers. For example add getUserForm() in the #ControllerAdvice annotated class, instead of your original controller(s):
#ControllerAdvice
public class ModelAttributeAdvice {
#ModelAttribute
public SearchForm getUserForm(){
return new UserForm();
}
}

Spring MVC: Log Controller paths without params

I am trying to add some metric gathering to a Spring MVC app. Lets say I have a controller whose mapping is:
/User/{username}/Foobar
I want to gather metrics on all controller mapping invocations with the path. Right now I can create a handler/interceptor and look at the requests but that will give me:
/User/Charlie/Foobar
Which is not what I want. I want the controller mapping itself to log. and I don't want to have to add something to every controller. I'd also rather not use AOP if I can help it.
It turns out that Spring hangs the best matching controller pattern on the request itself. You can get this from within a handlerinterceptor like this:
(String)request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)
I can think of two choices:
It seems to me the results of the matching are obtained in the class org.springframework.web.servlet.handler.AbstractUrlHandlerMapping, which logs the patterns obtained (see line 266). I'd try enabling logging for that class and see if the output is helpful for your purposes.
(Complicated)
Extending org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping to override the lookupHandler method inherited from AbstractUrlHandlerMapping and logging/registering what you need. Accoding to this class documentation, you can register a different one so that the DispatcherServlet uses your version.
In Spring 3.2.x DefaultAnnotationHandlerMapping is deprecated so, a different class would have to be used.

Base controller, setting a private member variable, is this safe?

I want to create a base controller class that my other controllers will inherit from. I have a simple public api that takes the authentication token via the query string, so I want to do this:
public class MyBaseController {
private String token = "";
public MyBaseController() {
}
}
And then my real controller would be like:
#Controller
#RequestMapping("/api/users")
public class UserController extends MyBaseControler {
// controller methods here
}
My question is, how can i get access to the HttpServletRequest in my base controller, and get the querystring parameter "?token=abc123" value and set the token var with the value.
Is this thread safe? It is my understanding that there will be a new controller instance per request correct?
Your controllers are better off if they're stateless.
You can inject them with Spring services as needed, but I don't see any reason why they have to hang onto the value of the token as a member variable.
It's far more likely that you'll want to store the token in session scope. I think your idea is wrong-headed.
I'll point out that Spring itself has moved away from inheritance for controllers. They're all annotation-based now, with no common base class or interface. Why do you think devolving back to the design they abandoned is a good thing?
You don't need a base controller, either.

Can I find the URL for a spring mvc controller in the view layer?

I think what I need is called reverse url resolution in Django. Lets say I have an AddUserController that goes something like this:
#Controller
#RequestMapping("/create-user")
public class AddUserController{ ... }
What I want is some way to dynamically find the url to this controller or form a url with parameters to it from the view (JSP), so I don't have to hardcode urls to controllers all over the place. Is this possible in Spring MVC?
Since Spring 4 you can use MvcUriComponentsBuilder.
For the most type-safe method:
String url = fromMethodCall(on(MyController.class).action("param")).toUriString();
Note this example requires that the method returns a proxyable type - e.g. ModelAndView, not String nor void.
Since 4.2, the fromMappingName method is registered as a JSP function called mvcUrl:
Login
This method does not have the proxy restriction.
Have you considered having a bean that aggregates all of the controller URLs you need into a HashMap and then adding this controller/URL Map to any model that requires it? Each Spring controller has the ability to call an init() method, you could have each controller add it's name and URL to the controller/URL map in the init() methods so it would be ready to use when the controllers go live.
Can solve with Java Reflection API. By Creating Custom Tag library. methods looks like this
Class c = Class.forName("Your Controller");
for(Method m :c.getMethods()){
if(m.getName()=="Your Method"){
Annotation cc = m.getAnnotation(RequestMapping.class);
RequestMapping rm = (RequestMapping)cc;
for(String s:rm.value()){
System.out.println(s);
}
}
}
Possible Problem You Can Face is
1.Path Variable > Like this /pet/show/{id} so set of path name & value should be support then replace this String.replace() before return url
2.Method Overriding > only one method is no problem. if Method override Need to give support sequence of Parameter Type That you really want like Method.getParametersType()
3.Multiple Url to Single Method> like #RequestMapping(value={"/", "welcome"}). so easy rule is pick first one.
4.Ant Like Style Url > Like this *.do to solve this is use multiple url by placing ant like style in last eg. #RequestMapping(value={"/pet","/pet/*.do"})
So Possible link tag style is
<my:link controller="com.sample.web.PetController" method="show" params="java.lang.Integer">
<my:path name="id" value="1" />
</my:link>
Where parmas attribute is optional if there is no method override.
May be I left to think about some problem. :)
I would probably try to build a taglib which inspects the annotations you're using in order to find a suitable match:
<x:url controller="myController">
<x:param name="action" value="myAction"/>
</x:url>
Taglib code might be something roughly like
Ask Spring for configured beans with the #Controller annotation
Iterate in some suitable order looking for some suitable match on the controller class or bean name
If the #RequestMapping includes params, then substitute them
Return the string
That might work for your specific case (#RequestMapping style) but it'll likely get a bit hairy when you have multiple mappings. Perhaps a custom annotation would make it easier.
Edit:
AbstractUrlHandlerMapping::getHandlerMap, which is inherited by the DefaultAnnotationHandlerMapping you're most likely using, returns a Map of URL to Handler
Return the registered handlers as an
unmodifiable Map, with the registered
path as key and the handler object (or
handler bean name in case of a
lazy-init handler) as value.
So you could iterate over that looking for a suitable match, where "suitable match" is whatever you want.
You can get access to the request object in any JSP file without having to manually wire in or manage the object into the JSP. so that means you can get the url path off the request object, have a google into JSP implicit objects.
Here is a page to get you started http://www.exforsys.com/tutorials/jsp/jsp-implicit-and-session-objects.html
The problem with this is that there's no central router in SpringMVC where all routes are registered and ordered. Then reverse routing is not a static process and route resolution in the view layer can be hard to integrate.
Check out this project for a centralized router (like rails) and reverse routing in the view layer.

Categories