Accessing controlleradvice objects from another controller - java

I've got a #ControllerAdvice class what I use to set user profile information all across the application. In this way, I get the user profile in every single JSP. However, I'm trying to access to that object in a #Controller like this with no success:
#ControllerAdvice
public class CommonControllerAdvice {
#ModelAttribute("PROFILE")
public Profile populateUserProfile(HttpSession session){
return (Profile) session.getAttribute("PROFILE");
}
}
#Controller
public class ActivityController {
#GetMapping("/view/activity/{id}")
public ModelAndView getActivity(ModelAndView modelAndView, #PathVariable Integer id) {
Profile profile = (Profile) modelAndView.getModel().get("PROFILE");
... ...
}
}
But I only get a NullPointerException because the profile is null. However, I know that it is not null because I can use it in the related JSP.

I found a solution. Just pass as a parameter the #ModelAttribute defined in the #ControllerAdvice class, instead of trying to obtain it from the ModelAndView.
#Controller
public class ActivityController{
#GetMapping("/view/activity/{id}")
public ModelAndView getActivity (
ModelAndView modelAndView,
#ModelAttribute Profile profile,
#PathVariable Integer id) {
//Profile object is well populated
//However I don't understand why this model is empty
ModelMap model = modelAndView.getModelMap();
...
}
}
It solved my problem, and I am happy with the solution, nevertheless, I expected to be able to access to this information directly in the ModelMap, and it is empty. I would like to know why.

Related

Cant understand why RepositoryRestController does not work?

I use Spring Data Rest and I can not understand why my RepositoryRestController does not work. Its code:
#RepositoryRestController
public class Cntrl {
#Autowired
private UserDao userDao;
#RequestMapping(name = "/users/{id}/nameOne",method =
RequestMethod.GET)
#ResponseBody
public PersistentEntityResource setNameOne(#PathVariable("id") Long id, PersistentEntityResourceAssembler persistentEntityResourceAssembler){
User user = userDao.findById(id).orElseThrow(()->{
throw new ServerException("Wrong id");
});
user.setLogin("One");
userDao.save(user);
return persistentEntityResourceAssembler.toFullResource(user);
}
}
And Spring Boot start class:
#SpringBootApplication
#EnableWebMvc
#EnableScheduling
#EnableJpaRepositories
#EnableSpringDataWebSupport
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
When i go to base path (localhost:8080/api) everything is fine, but when send GET to request to localhost:8080/api/users/1/nameOne I get empty response, i dont have other controllers and I have user with id 1, so why it is not working ?
It doesn't work because the URL structure you are using has already a meaning in Spring Data Rest context.
/{repository}/{id}/{column} URL is handled at RepositoryPropertyReferenceController.followPropertyReference method.
/api/users/1/nameOne means: get the nameOne column of the user with the id of 1. An important note is that: this column should reference another #Entity. This means if you have a String column named "surname" and you hit the URL /api/users/1/name you will get 404 because this column is not referencing another entity. If you have a column named school which references to a School entity and you hit the URL /api/users/1/school you will get the referenced school entity for that user. If the user does not have a school then you will get 404 again.
Also, #RepositoryRestController can be used for #RequestMapping if the URL you are giving isn't colliding with Spring Data Rest.
You can test that with the following example:
#RepositoryRestController
public class CustomRepositoryRestController {
#RequestMapping(path = "/repositoryRestControllerTest", method = RequestMethod.GET)
#ResponseBody
public String nameOne() {
return "test";
}
}
Visit http://localhost:8080/repositoryRestControllerTest
I hope this explanation clarifies things for you.
If localhost:8080/api is your root context, then localhost:8080/api/users/1/nameOne should be the url you are using for the user GET.

Redirect between pages - Spring MVC

I am beginning with Spring Portlet MVC but I believe this question also applies to Spring MVC. Let me show you this example
#Controller
#RequestMapping(value="VIEW")//this maps to Portlet Mode VIEW
public class LoginController {
#RenderMapping("action=showRegisterPage")
//this annotation is almost the same like #RequestMapping
public String showRegisterPage() {
return "registration";
}
#ModelAttribute("loginForm")
public LoginForm getLoginForm() {
return new LoginForm();
}
}
and then controller for registration page
#Controller
#RequestMapping(value="VIEW")
public class RegistrationController {
#ModelAttribute("user")
public User getUser() {
return new User();
}
}
And the problem is, when I call the showRegisterPage method (action) from page I get redirected to registration.jsp but this exceptions occurs
Caused by: java.lang.IllegalStateException: Neither BindingResult nor
plain target object for bean name 'user' available as request attribute
This can be fixed by putting ModelAttribute user to LoginController but code duplication is not really right way, so how can I solve this correctly?I have also tried moving showRegisterPage method to RegistrationController but then the link is not working. Maybe somebody could elaborate how exactly binding of page to some controller works?
P.S. Both index.jsp and registration.jsp contains <form:form> tag and I also omitted boilerplate code from both controllers.
Exception is thrown because when you go to the registration page model doesn't have user attribute. I guess that your register page contains smth like this
<form:form commandName="user" >
That's why when you paste this code to first controller error is gone
#ModelAttribute("user")
public User getUser() {
return new User();
}
this code provide user object to model.
If you not want copy one method two times you can
#RenderMapping("action=showRegisterPage")
//this annotation is almost the same like #RequestMapping
public String showRegisterPage(Model model) {
model.addAttribute("user", new User());
return "registration";
}
In this case model will be contain user object.
Ideally the showregistrationpage method should b part of the registration controller rather than the login controller.

Overloading a spring controller method with the same request mapping

I have a session attribute : user, and I have a url that I want to be viewed by both logged in users and publically by people not logged in as a user.
So what I want to do is this :
#Controller("myController")
#SessionAttributes({"user"})
public class MyController {
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id) {
return modelandview1;
}
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id, #ModelAttribute User user){
return modelandview2;
}
However, I have a feeling its not going to work ... suggestions very welcome.
You only need the second method, the one that takes the User agument as well. When it's called without request attributes available to populate the User model, you'll just get a User instance with all null (or all default) field values, then in the body of the method you treat each situation accordingly
I don't think it's a right case for #SessionAttributes. This annotation is usually used to keep original instance of a form-backing object, to avoid passing irrelevant parts of its state via hidden form fields.
Your sceanrio is completely different, thus it would be better to use HttpSession explicitly:
#RequestMapping(value = "/MyPage/{id}", method = RequestMethod.GET)
public ModelAndView getPage(#PathVariable Integer id, HttpSession session) {
User user = (User) session.getAttribute(...);
if (user != null) {
...
} else {
...
}
}
Also note that #ModelAttribute is a subject to data binding - user can change its fields by passing request parameters. You definitely don't want it in this case.

Spring MVC - Variables between pages, and Unsetting a SessionAttribute

The question sounds weird, I'm playing around with Spring MVC and am trying to move between two pages and basically I'm creating a JSP page using Spring Form JSTL's so it just uses a POST, and I use a controller to move from one page to the next. But Models are lost from page to page, and I'd like to hide the actual variable so QueryStrings are out of the question(as
far as I know). I know I can use a InternalResourceView, but only allows me to use a model.
I want to transfer a variable that will be exclusive to that page, what's the best way without a model or using QueryStrings?
I was planning on using SessionAttribute to easily define them, but was wondering, how do you remove a SessionAttribute created variable? I tried HttpSession.removeAttribute and it didn't seem to work.
You can also use SessionStatus.setComplete() like this:
#RequestMapping(method = RequestMethod.GET, value="/clear")
public ModelAndView clear(SessionStatus status, ModelMap model, HttpServletRequest request) {
model.clear();
status.setComplete();
return new ModelAndView("somePage");
}
or DefaultSessionAttributeStore.cleanUpAttribute like this:
#RequestMapping(method = RequestMethod.GET, value="/clear")
public ModelAndView clear(DefaultSessionAttributeStore status, WebRequest request, ModelMap model) {
model.remove("mySessionVar");
status.cleanupAttribute(request, "mySessionVar");
return new ModelAndView("somePage");
}
I use it like this on one of my forms that has mulitple sessionAttributes and I want to remove only one of them.
Yes... HttpSession.removeAttribute
You can use the removeAttribute method from the HttpSession class.
you can use WebRequest.removeAttribute(String name, int scope) that works with Spring #SessionAttributes. Quote from #SessionAttributes javadoc - "Alternatively, consider using the attribute management capabilities of the generic {#link org.springframework.web.context.request.WebRequest} interface."
Also look at my example.
#Controller
#SessionAttributes({"sessionAttr"})
public class MyController {
#ModelAttribute("sessionAttr")
public Object defaultSessionAttr() {
return new Object();
}
#RequestMapping(value = "...", method = RequestMethod.GET)
public String removeSessionAttr(WebRequest request, Model model) {
request.removeAttribute("sessionAttr", WebRequest.SCOPE_SESSION);
model.addAttribute("sessionAttr", defaultSessionAttr());
return "myView";
}
}

How to create a default method in SpringMVC using annotations?

I can't find a solution to this, and it's driving me crazy. I have #Controller mapped that responds to several methods using #RequestMapping. I'd like to tag one of those methods as default when nothing more specific is specified. For example:
#Controller
#RequestMapping("/user/*")
public class UserController {
#RequestMapping("login")
public String login( MapModel model ) {}
#RequestMapping("logout")
public String logout( MapModel model ) {}
#RequestMapping("authenticate")
public String authenticate( MapModel model ) {}
}
So /user/login -> login method, /user/logout -> logout, etc. I'd like to make it so that if someone goes to /user then it routes to one of these methods. However, I don't see anything on #RequestMapping that would allow me to specify one of these methods as a default handler. I also don't see any other annotations that might be used on the class either to do this. I'm beginning to suspect it doesn't exist.
I'm using Spring 2.5.6. Is this solved in 3.0.0? I might just hack Spring to make it work because it's tremendously annoying this isn't more straightforward.
Thanks in Advance.
Take a look at this answer:
Spring MVC and annotated controllers issue
What if you annotate a method with:
#RequestMapping(method = RequestMethod.GET)
You can see an example here:
Spring 3.0 MVC + Hibernate : Simplified with Annotations – Tutorial
The same behavior can be seen here:
Spring Framework 3.0 MVC by Aaron Schram (look at page 21)
Short answer: I do not know how to simply specify one method as default with a simple tag.
But there is this ...
I do not know in which version of Spring this was implemented, but you can provide multiple values to #RequestMapping in 3.1.2. So I do this:
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(value = {"", "/", "/list"}, method = RequestMethod.GET)
public String listUsers(ModelMap model) { }
#RequestMapping(value = "/add", method = RequestMethod.POST)
public ModelAndView add(HttpServletRequest request, ModelMap model) { }
}
The following URLs then map to listUsers():
http://example.com/user
http://example.com/user/
http://example.com/user/list
I would create one default method without RequestMapping's value in there. Please see method defaultCall() below. You can then simply call them with URL: [yourhostname]/user
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(method = RequestMethod.GET)
public String defaultCall( MapModel model ) {
//Call the default method directly, or use the 'forward' String. Example:
return authenticate( model );
}
#RequestMapping("login")
public String login( MapModel model ) {}
#RequestMapping("logout")
public String logout( MapModel model ) {}
#RequestMapping("authenticate")
public String authenticate( MapModel model ) {}
}
Ref: Spring Framework Request Mapping
Simply using #RequestMapping("**") on your default method should work. Any more specific mappings should still pick up their requests. I use this method for setting up default methods sometimes. Currently using Spring v4.3.8.RELEASE.

Categories

Resources