Announcement Announcement Module
Collapse
No announcement yet.
Problem with @SessionAttributes and @ModelAttribute Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    Originally posted by pushdown View Post
    Can be there more ModelAttributes?
    If your question refers to the concept of having multiple ModelAttributes as params to the handler method, I think the answer is No. I haven't tried it, but there is no way to bind multiple objects to a single form and no way to submit multiple forms at once, so I don't think so. You can, however, have a single form backing object that contains multiple objects and then have your form populate them all, either by iterating over a collection or by explicitly referring to each one by name or index, appropriate.

    You can have multiple ModelAttributes for use by the view layer to render content into the view, of course, but only one can be associated with a form.

    Comment


    • #17
      Hi,

      I understand the following code would work if your GET method is displaying an empty form. How would you do it if your GET method is displaying an existing entity - i.e. if you need to pass in a userId to the GET handler?

      Thanks in advance.


      Code:
          @ModelAttribute("user")
          public User populateUser() {
              log.debug("creating empty user");
              return new UserImpl();
          }
          
          @RequestMapping(method = RequestMethod.GET)
          public String setupForm(Model model) {
              log.debug("processing login GET method");
              return "login";
          }

      Comment


      • #18
        Originally posted by goofy View Post
        Hi,

        I understand the following code would work if your GET method is displaying an empty form. How would you do it if your GET method is displaying an existing entity - i.e. if you need to pass in a userId to the GET handler?

        Thanks in advance.
        Pretty much exactly how you'd think. Instead of returning an empty user object, you'd populate it first. For example, if the user is logged in, I'm going to assume you have some method by which you can get their user id from the session or the SecurityContext. So if you inject the the necessary id into the controller or provide a mechanism to retrieve it (You can maybe even just specify the HttpRequest or HttpSession as parameters to the populateUser() method, and then load a user object from persistent storage within that method. You can inject any of the dependencies you may require directly into the controller if you need access to a service or dao object.

        Code:
        @ModelAttribute("user")
        public User populateUser(HttpSession session) {
            userId = AppUtils.getAuthenticatedUserId(session);
            User u = userService.loadUser(userId);
            return u;
        }
        If you are using SpringSecurity, you can easily fetch the userId via SecurityContextUtils instead of grabbing it from the session.

        In my case, I have a factory method that knows how to fetch a User object via the id stored in the session. I create a spring bean via that factory method that has session scope (see docs for details - chapter 3 I think) and then I inject that authenticatedUser bean into any controller that requires authentication. I have my spring security filters set up such that the filters necessary to put the userId where I need it are called for any controller that may require it. It works very nicely and prevents me from having to store a User object directly in the session, where I'd have to worry about serializing lazy loaded collections when distributing the session across the cluster.

        Of course, if you are editing some user object that isn't the logged in user, you'll have to fetch the id from the HttpRequest, and then load that user from the db via your service layer.

        Comment


        • #19
          Actually, I've got an even simpler solution in the context of a GET method, since you aren't worried about binding query parameters with your user object.

          Something like this:

          Code:
          @RequestMapping(method = RequestMethod.GET)
          public ModelAndView setupForm(HttpRequest request) {
              userId = request.getAttribute("userId");
              User u = service.loadUserById(userId);
              return new ModelAndView("formView", "user", u);
          }
          I don't have javadocs in front of me and I'm not sure of the exact syntax for creating a ModelAndView object, but you can look that up. You should get the gist from the above code.

          Comment


          • #20
            This sounds like what I need. Thanks.

            Comment


            • #21
              Hi,

              I have in my spring configuration file :

              - a bean which is session-scoped

              Code:
              <!-- **************	-->
              	<!--	context		-->
              	<!-- **************	-->
              	<bean id="myBeanInSession" class="blabla.BeanInSession"
              	scope="session">
              	</bean>
              - an interceptor :
              Code:
              <!-- ************ -->
              	<!--  interceptor -->
              	<!-- ************ -->
              	<bean name="securityInterceptor" class="web.interceptor.SecurityInterceptor">
              	</bean>
              I have annotate-based controllers like this :
              Code:
              @Controller
              @SessionAttributes("myBeanInSession")
              public class GetContentController {
              	
              	@RequestMapping(value="/content.get", method=RequestMethod.GET)
              	public String get(ModelMap model, @RequestParam("path") String path,
              			@RequestParam(value="mode", required=false) String mode,
              			@ModelAttribute("myBeanInSession")  BeanInSession myBeanInSession,
              			@ModelAttribute("getContent") GetContent getContent) {
              ...
              }
              If in the securityInterceptor i put the code :

              Code:
              public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
              			Object arg2) throws Exception {
              		this.beanFactory.getBean("myBeanInSession");
              ...
              }
              I have no problem.

              But if I don't add code
              Code:
              this.beanFactory.getBean("myBeanInSession");
              in the preHandle method of the interceptor I have the error :

              Session attribute "myBeanInSession" required - not found in Session
              I also noticed that the instance of myBeanInSession is added in request.getSession().getAttribute("myBeanInSession ") by the method this.beanFactory.getBean("myBeanInSession");

              So it seems there is a relation between session-scoped beans, HttpSession.getAttribute() and @SessionAttributes annotation but I don't really understand how it works...

              I wonder if it is not because of side effects that my code works but that it would be a better practice not to use @SessionAttributes annotation for referring to session-scoped beans. But then how could i refer session-scoped beans in my annotated controllers methods ?

              Thanks

              G. Dony

              Comment


              • #22
                Ok I finally forget @SessionAttributes annotation and HttpSession.getAttributes() or WebRequest.getAttributes().

                My session-scoped bean is declared in my spring xml configuration file like this :

                Code:
                <bean id="myBeanInSession" class="blabla.BeanInSession" scope="session">
                		<aop:scoped-proxy/>
                	</bean>
                And in my annotated controller I encapsulate ma session-scoped bean like this :

                Code:
                 private BeanInSession beanInSession;
                	
                	@Autowired
                	public GetContentController(BeanInSession beanInSession) {
                		this.beanInSession= beanInSession;
                	}
                Now I can use my session-scoped bean in any method of my controller.

                But I still wonder why when using WebRequest.getAttributes("myBeanInSession" , SESSION_SCOPE) (or HttpSession.getAttribute("myBeanInSession")) the bean is not accessible except after invoking beanFactory.getBean(). Is there a lazy initialisation of session-scoped beans ?

                Comment

                Working...
                X