Announcement Announcement Module
Collapse
No announcement yet.
success messages - redirect after post Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • success messages - redirect after post

    In Matt Raible's "comparing web frameworks presentation", he notes:

    Struts is the only framework that allows success messages to live through a redirect
    In Spring, can't you just add "messages" as bound parameters in a model map and return a populated RedirectView descriptor? What am I missing? What should be enhanced?

  • #2
    Well, if it's what I think it is, I do something like:
    Code:
            Map model = new HashMap();
            model.put("employeeId", employee.getEmployeeId());
            model.put("saveSuccessful", new Boolean(true));
    My browser then has the URL:
    Code:
    ?employeeId=0&saveSuccessful=true
    And I use the saveSuccessful=true to then display the (i18n) success message.

    Comment


    • #3
      I just whipped up something to help with this...

      for example, in onSubmit(..):
      Code:
      model.put(SessionMessage.RESULT, SessionMessage.create(request, "user.userEmail.edit.success"));
      which puts a key in the model with a unique id referencing this message, so you end up with a redirect url like "?result=234".

      That key is stored in the HttpSession until a HandlerInterceptor automatically pulls out that message and adds it to the model:

      Code:
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              if (okToPopulate(modelAndView, request)) {
                  Map model = modelAndView.getModel();
                  model.put(RESULT_MESSAGE_KEY, 	SessionMessage.retrieveMessage(request, SessionMessage.RESULT));
                  model.put(ERROR_MESSAGE_KEY, 		SessionMessage.retrieveMessage(request, SessionMessage.ERROR));
              }
          }
      So I end up with the variable ${resultMessage} on every jsp page. retrieveMessage() uses the stored code to look up the actual text with a MessageResolver. It also removes the value from the HttpSession since this type of message should only be shown once.

      I just have a jsp include file that prints ${resultMessage} if it's set. Nice and easy, and keeps the url clean.

      Comment


      • #4
        its a simple as:
        Code:
        return new ModelAndView(new RedirectView(getSuccessView())).addObject(Constants.STATUS_MESSAGE,"updated");

        Comment


        • #5
          So I end up with the variable ${resultMessage} on every jsp page. retrieveMessage() uses the stored code to look up the actual text with a MessageResolver. It also removes the value from the HttpSession since this type of message should only be shown once.
          Your approach is very interesting. Could you post the code for the SessionMessage class? And if you could post the way you configure the use of the postHandler()-method in appropriate .xml?

          Very grateful!

          Comment


          • #6
            RedirectView & passing model

            This doesnt seem to work as I expect: -

            onSubmit
            Code:
            map=errors.getModel()
            // ...some logic
            map.put("id", domain.getId());
            map.put("statusMessage", "save"); 
            return new ModelAndView(new RedirectView("some.action"), map)

            formBackingObject

            Code:
            String id = request.getParameter("id"); 
            if (StringUtils.isNotEmpty(id)) 
            {
             return loadExistingCommand(id);
            }
            else { 
             return createDefaultCommand();
            }
            It does redirect with the command object (based on the request param id). However the statusMessage is always empty.

            When, I do this, instead
            onSubmit
            Code:
             
            map.put("id", agreement.getId());
            map.put("statusMessage", "save"); 
            return showForm(request, errors, getSuccessView(), map);
            it works for the first return, but later on some of my values on the screen don't show up (like listoptions etc from the command object)

            Comment


            • #7
              I personally like to use properties for my messages so I typically to something like this (really basic example):
              Code:
              Controller:
              model = new HashMap();
              model.put(UIConstants.MODEL_MESSAGES,"message.saved");
              return new ModelAndView("fooRedirect", model);
              
              Messages.properties:
              message.saved=Your record was saved
              
              Redirected View's Controller:
              String msgCode = request.getParameter(UIConstants.MODEL_MESSAGES);
              		
              if (msgCode != null) {
              	try {
              				
              		String message = getMessageSourceAccessor().getMessage(msgCode);
              		model.put(UIConstants.MODEL_MESSAGES,message);
              			
              	} catch (NoSuchMessageException e){
              		//log that no message found for this code
              	}
              }

              Comment


              • #8
                I think you've read my question wrong. I do use message properties ("save" above is a key to the message properties file and I use <fmt> tags). In my case redirectView redirects to the same controller. So the 'get' semantics are called. ie. formBackingObject, initbinder ... return ModelAndView(). The only way I can think of writing this logic is formBackingObject but it has only parameter - httpRequest

                Comment


                • #9
                  Originally posted by infinity2heaven
                  I think you've read my question wrong.
                  What question did you pose?
                  I don't seem to see one.
                  Last edited by jglynn; Oct 4th, 2007, 05:07 PM.

                  Comment


                  • #10
                    I got this working, would be interested to know if this is an elegant solution.
                    From the code I'd given in the previous replies, instead of writing the code map.put("statusMessage", "save"); in onSubmit, add the same in referenceData() method.

                    It would be interesting to understand the flow with a RedirectView:-
                    1. User clicks on something, form submits
                    2. onSubmit, process/save and return a RedirectView with a map of id and success message.
                    3. Now, the redirect could be to the same controller or a new controller; whatever that is, it is a http get call and not a post; hence, the order of sequence is formBackingObject, initBinder, referenceData before returning ModelAndView. In formBackingObject, id would be checked, reloading the object again. But where do we get the statusMessage?
                    4. the only other 'hook' which has an errors object is referenceData. So you add the code here

                    Now, whatever you've added in the 'map' before returning RedirectView would be available with the new request

                    Code:
                    String status = map.put("statusMessage", request.getParameter("statusMessage"));
                    Last edited by infinity2heaven; Oct 8th, 2007, 04:59 PM.

                    Comment


                    • #11
                      A simple interceptor does the trick

                      The following interceptor does the trick for us. It determines, if to redirect or not by looking at the view name. If so, it saves the model and the view name without the prefix into the HttpSession. Then it constructs a redirect url with the id of the saved model.
                      The same interceptor is used to reconstruct the model by looking at the request parameters.
                      The only caveat is, that lazy-loading doesn't work for the saved model, because a new request has started and the hibernate session the objects in the model point to is already closed.
                      The only even more transparent way I can imagine is to use a ServletFilter that saves the generated response to the session and outputs a redirect instead. After the redirect has happend, the previously saved response is sent to the browser. That should solve all Spring MVC problems with redirect-after post.

                      The code
                      Code:
                      import java.rmi.server.UID;
                      import java.util.Date;
                      
                      import javax.servlet.http.HttpServletRequest;
                      import javax.servlet.http.HttpServletResponse;
                      import javax.servlet.http.HttpSession;
                      
                      import org.springframework.web.servlet.ModelAndView;
                      import org.springframework.web.servlet.ModelAndViewDefiningException;
                      import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
                      import org.springframework.web.servlet.view.RedirectView;
                      
                      public class RedirectInterceptor extends HandlerInterceptorAdapter
                      {
                        private String viewPrefix;
                        private String sessionAttributePrefix;
                        private String requestParameterName;
                        
                        private static class RedirectInfo
                        {
                          long timestamp;
                          ModelAndView modelAndView;
                        }
                      
                        public boolean preHandle(
                          HttpServletRequest request,
                          HttpServletResponse response,
                          Object handler)
                        throws Exception
                        {
                          // Are we coming from a redirect?
                          if ("GET".equals(request.getMethod()) && request.getParameter(requestParameterName)!= null)
                          {
                            // reconstruct name of the RedirectInfo-Object
                            String attributeName=sessionAttributePrefix+request.getParameter(requestParameterName);
                            HttpSession session=request.getSession(false);
                            // go to the originally wanted view
                            RedirectInfo info=null;
                            if (session != null && session.getAttribute(attributeName) != null)
                            {
                              info = (RedirectInfo) session.getAttribute(attributeName);
                              session.removeAttribute(attributeName);
                              throw new ModelAndViewDefiningException(info.modelAndView);
                            }
                          }
                          return true;
                        }
                      
                        public void postHandle(
                          HttpServletRequest request,
                          HttpServletResponse response,
                          Object handler,
                          ModelAndView modelAndView)
                        throws Exception
                        {
                          // Do we want to redirect?
                          String viewName=getActualViewName(request, modelAndView);
                          if (viewName != null)
                          {
                            // remember the model and the view and put it into the session
                            ModelAndView sessionMAV=new ModelAndView(viewName, modelAndView.getModel());
                            String id=getNewId();
                            String attributeName=sessionAttributePrefix+id;
                            RedirectInfo info=new RedirectInfo();
                            info.timestamp=new Date().getTime();
                            info.modelAndView=sessionMAV;
                            HttpSession session=request.getSession();
                            session.setAttribute(attributeName, info);
                            // modify model and view in order to let a redirect happen
                            modelAndView.clear();
                            RedirectView rv= new RedirectView(getOwnUrl(request));
                            modelAndView.setView(rv);
                            modelAndView.getModel().put(requestParameterName,id);
                          }
                        }
                      
                        // Returns null, if no redirect should happen, otherwise the view name without prefix
                        private String getActualViewName(HttpServletRequest request, ModelAndView modelAndView)
                        {
                          String viewName=null;
                          // POST and prefix trigger a redirect
                          if ("POST".equals(request.getMethod()))
                          {
                            if (modelAndView != null && 
                                modelAndView.getViewName() != null && 
                                modelAndView.getViewName().startsWith(viewPrefix))
                            {
                              viewName=modelAndView.getViewName();
                              viewName=viewName.substring(viewPrefix.length());
                            }
                          }
                          return viewName;
                        }
                      
                        private String getNewId()
                        {
                          return new UID().toString();
                        }
                        
                        private String getOwnUrl(HttpServletRequest request)
                        {
                          String url=request.getServletPath();
                          if(request.getPathInfo() != null)
                          { 
                            url+=request.getPathInfo();
                          } 
                          url=url.substring(url.lastIndexOf('/')+1);
                          return url;
                        }
                      
                        public void setRequestParameterName(String string)
                        {
                          requestParameterName = string;
                        }
                      
                        public void setSessionAttributePrefix(String string)
                        {
                          sessionAttributePrefix = string;
                        }
                      
                        public void setViewPrefix(String string)
                        {
                          viewPrefix = string;
                        }
                      
                      }
                      How to configure it:
                      Code:
                      	<bean id="redirectInterceptor" class="RedirectInterceptor">
                      		<property name="viewPrefix" value="RAP:"/>
                      		<property name="sessionAttributePrefix" value="RAP"/>
                      		<property name="requestParameterName" value="RAP"/>
                      	</bean>
                      How to use it in a controller:
                      Code:
                      <property name="successView" value="RAP:yourview"/>
                      HTH
                      Fokko

                      Comment


                      • #12
                        Anyone figured out how to do this in RC2?

                        I've tried using an instance of RedirectView and setting setExposeModelAttributes to false and redirectView.addStaticAttribute("success", "success");

                        I don't understand the API because this does not work and it the model just get's cleared when it hits the next handler method.

                        is the only way to do this really to use an interceptor? really?

                        Comment


                        • #13
                          Session less solution

                          Hi,

                          Does anyone have a generic Spring 3.0 solution that doesn't involve the session? I like to keep the session out of this for performance reasons.

                          Kind regards,

                          Marc

                          Comment

                          Working...
                          X