Post/Redirect/Get pattern for web applications

Discussions

J2EE patterns: Post/Redirect/Get pattern for web applications

  1. Being able to refresh a web page in the web application is an important usability feature. Also it is an MVC concept showcase: web page is a view for the underlying model, it is a window through which a user looks at the data.
     
    Two most popular browsers: MSIE and Netscape/Mozilla have different names for the page refresh operation. IE uses more user-friendly "refresh", while Netscape uses more technical "reload". I prefer "reload" as a more correct term, but in fact both reload and refresh operations take place when a user tries to renew the page in the browser.

    Very often a user is completely unaware that browser resends information to the server during page reload. Even if a browser warns user that information should be resent to the server, a user often cannot understand the technical meaning of the warning. Let us trace what happens during standard form submission process.

    * The first page containing an input form could be obtained from the server using either GET or POST. If GET was used, then a user can refresh this page without seeing nagging "Information must be resend" message. Still, if this GET has attached parameters, they would be resent to the server.
    * After a user fills out the form, it is submitted to the server. POST method is usually used for sending forms.
    * Server responds with a result page.

    The result page may look completely innocent, with no forms or any active elements, just some boring results from the database. For anyone remote from the internet development it seems completely legal and easy to refresh this page. Most people think that word "refresh" means exactly what it sounds: reload the page with its data from the server.

    But in fact to refresh the result page would mean to re-issue the same request to the server which was used to obtain the page at the first place. That means, to do the whole POST of the form from the first page again. Here is where a user sees the "Information must be resend" message. Resending of the information may produce unwanted or inconsistent modifications of underlying data. Thus, these form resubmits must be properly handled by controller and model.

    Now we are getting to the problem: because browser needs to resend the whole original request to obtain the result page, the refresh operation is not really MVC-compliant because user data is sent to the server. The solution of problem is quite simple – redirection.

    This technique is actually pretty well known, but it is not standard yet for "after-post" results, and it does not have a well-known name. I call it "PRG request pattern", because it consists of three major parts: Post, Redirect and Get. The first one actually is not necessarily a POST, it is just that POST is most often used to send user data to the server. The last one must be GET so user would not see nagging messages. PRG request works in two stages:
    * First a user-filled form is sent to the server using either POST or GET method. Server stores the information, updates the database and business model data, and replies with REDIRECT response for a View page.
    * Browser loads View using GET, no user data is sent to the server at this point.

    Now we have a clean MVC solution. When a user tries to refresh a result page, browser resends to the server "empty" GET request. All needed data is already on the server, so server just fills it in the page and sends back to the user.

    What about back button? This works too, it returns us one step backwards, to the page with a form. This form can be refreshed as well. If it was obtained using GET, then user would not see warning message.

    Page refresh works perfectly with redirection. We have to do the roundtrip to the browser, but one more second spent for redirection means little for interactive applications.
     
    The only thing that have to be dealt with is intentional form resubmitting which happens when a user returns to the first page using Back button and submits form again. This should be checked by controller and a model and is out of scope of this pattern.

    Bottom line: the PRG pattern provides the following benefits:
    * it separates the View from Model updates;
    * result page refresh does not cause form resubmit;
    * page refresh is done using GET, so no messages are shown to a user

    Threaded Messages (37)

  2. It's nice to see someone post this pattern. It is an approach that I have advocated for a while now, and often face some resistence to.

    I do think it is important to list the limitations of this pattern though. Namely, unlike server forwarding operations, there is no easy mechanism to pass data between the component processing the POST (the controller) and the view. In fact, there are only two options:

    1. Store data in the session.
    2. Passing values as request parameters in the redirect operation:

         /<viewURL>?param1=value1&param2=value1

    Despite this limitation, I think this is the correct approach to view invocation. In particular, properly applied, you can use this approach to make the view independent of the controller. All you need to do is ensure that all the information need to render the view should be passed as request parameters.
  3. Parameters passing[ Go to top ]

    <Paul>
    All you need to do is ensure that all the information need to render the view should be passed as request parameters.
    </Paul>
    This is exactly what I am against of. From the rendering standpoint both operations shold be idempotent: (1) show the view right after form was posted and (2) show the view if refresh is requested. Any data the browser want to send to the server should be send during POST and kept on the server for all future view renderings. Where this data should be kept and when it should be removed is a different issue.

    The point is, I want to be able to press F5 as many times as I want, and still get the properly rendered page filled with actual data. Basically, I am talking about updating Model during Post, and rendereing Model during Get. If Model was not changed, I should receive the same page, as 5 minutes ago.
  4. Parameters passing[ Go to top ]

    I was not advocating sending *all* the information in the original POST to the view. Just enough information to render the page. Consider this example (using servlets and JSP, but the pattern applies to other web development technologies).

    1. An "Edit Item" HTML form is submitted to an EditItemServlet via the POST method, including all item data.

    2. The EditItemServlet loads the corresponding item object, updates its information and saves it.

    3. The EditItemServlet sends a redirect operation [response.sendRedirect()] to a showItem.jsp, passing the primary key value as a parameter:

        showItem.jsp?id=#

    4. The showItem.jsp retrieves the id parameter, loads the (possibly cached) item object using its id and displays its information.

    The id parameter is important. It forms the conversational state between the client and server. Without it, the server has no easy way to know what information the client wants displayed. The server would have to retain this information in the session, and then your application would need to deal with session-timeout issues, clients that don't support cookies and so forth.

    Another advantage of this approach is that the view is more reusable:

    1. You could target the showItem.jsp page directly from a hyperlink, without first invoking the EditItemServlet. For example, from a page listing a bunch of items.

    2. A "New Item" HTML form could target a CreateItemServlet that creates an item, auto-generates its id, inserts the results in the database and redirects to the same showItem.jsp used to render existing items.
  5. Re: Parameters passing[ Go to top ]

    Yes, you are right, some form of ID is necessary to identify a View. I suppose it would be "View ID" or "View Name". I did not stress that out, because View identification can be made by the url resourse name itself, without need to pass the additional parameter. But parameter makes it easier to show data, say, from a database.
  6. Very often a user is completely unaware that browser resends information to the server during page reload.


    This should be learned in a basic school ;-)
  7. Very often a user is completely unaware that browser resends information to the server during page reload.


    Very often a web application developer is completely unaware of the behaviour of the users he is developing for.

    Users ARE using Reload/Back/Forward buttons and so web applications must be able to handle this without generating inconsistencies.

    Because of that reason, patterns like this are quite important (but often ignored)!
  8. Users ARE using Reload/Back/Forward buttons and so web applications must be able to handle this without generating inconsistencies.


    perfectly right, but this pattern doesn't solve this problem completely:

    "...The only thing that have to be dealt with is intentional form resubmitting which happens when a user returns to the first page using Back button and submits form again. This should be checked by controller and a model and is out of scope of this pattern."

    So why the whole effort?

    The only problem this pattern solves completely is that it illuminates "Information must be resend" message.

    The price of the solution:
    - slow redirect
    - redefinition of standard behaviour of a web application (for internet aware users).

    But I agree that the name of the "Refresh" / "Reload" button is far from perfect. May be web browsers should change the name of the button dynamically in dependence on the last action...
  9. Seems like everything is being dressed up in patterns these days.
  10. Seems like everything is being dressed up in patterns these days.


    something like voting for patterns on TheServerSide would be nice, on my opinion.

    Thanks rating it would be possible to overfly inconsiderable patterns.
  11. redirect is slow[ Go to top ]

    1. redirect is slow, and must be used only when forward doesn't work
    2. what's a problem with something being "not MVC"? There's no MVC in web applications. can not be. I mean the "original MVC" like in Smalltalk.
  12. Another approach:
    http://www.robertocolmegna.it/java
  13. Small additions[ Go to top ]

    I use this pattern and find it very useful. Often it's necessary to return to the same page where user comes from before posting.
    The easiest way looks like:

    response.sendRedirect(request.getHeader("referer"));

    But it doesn't work always because some users use proxies that cut "referer" header. For this kind of users it's possible to use special custom tag inside the form that adds hidden field with URL of the page that contains the form (only if user's request doesn't contain "referer" header), say "backURL". Then we can use this:

    String s = request.getParameter("backURL");
    if(s == null)
      s = request.getHeader("referer");
    if(s == null)
      throw new Exception("unable to return back");
    response.sendRedirect(s);
  14. RE: Small additions[ Go to top ]

    Often it's necessary to return to the same page where user comes from before posting.
    The easiest way looks like:

    response.sendRedirect(request.getHeader("referer"));

    [...]
    Why don't you let the user click on the BACK button of his browser ???
    This is a very basic feature for a browser, and one of the first things a browser-user learns !! Don't you think ?
    HTTP is a very simple protocol ; web applications should aim to be as simple as possible. (I know it gets hard sometimes.)
    I agree 100% with the PRG pattern, I always use it. One should never be confronted to the "you're about to resend POSTDATA" message.

    Cheers.
  15. The Real Problem is this :[ Go to top ]

    I think, the real problem is when a form (POST-submitted) is not validated by the application. What should happen in this case ?

    From the user point of view, it's easy : we are expecting to see the form filled with the data previously entered (except passwds, maybe), and showing [in red] the error(s) that made the form not to be validated.

    But what happens if the user hits 'refresh' on this page ? Should he see the "resend POSTDATA" message ? I think not.
    That means that the application, after not validating the POSTed data, should send a REDIRECT, to the page that displays the form. And this is when an important part of the framework enters in game : the form should detect that the user is "back from error", and fill the form with the previously posted data, which would have been saved in session, before the REDIRECT. (The data should not be passed in the URL, since it may be very large.) So, two or three informations are put in session :
     - the previously posted data
     - all the error messages
     - a Boolean indicating that the form should be displayed in 'backFromError' mode, which can also be set as a request parameter in the URL.

    And what happens if the user hits the "refresh" button ? It depends on what the designer wants ; the form could display the same data again, or the initial blank form.

    An exception for this is the (quite rare) case where it is clearly specified that the application should not use cookies (no cookies = no session). Then, I suggest that the application should not send a REDIRECT after the post, but simply re-display the form with the data contained in the request.
    This is not PRG, but users should accept cookies ! cookies are good !

    I can give some simple practical examples if you want.

    Cheers
  16. On-Page Redirection is highly objectionable from Search Engine Optimization point of view. Most of the Web Sites fail to recognise the importance of their Main Page (Homepage). Several online companies and big multinational companies redirect their web sites onload. Whenever there needs to add a redirection feature, make sure that there is a gap interval of 5-7 sec, otherwise your website could be in trouble from Search Engine Optimization Point of View. regards Raj Web Site Promotion SEO Search Engine Optimization
  17. In WebWork2 I implemented a token tag which, in combination with the token Interceptor, will make sure that the form can only be submitted once. If a duplicate post comes in, there are 2 strategies for what to do (in my implementation):

    1) It returns an "error" code which is mapped to an error view for your Action

    2) It can save the processed state of the first form post in the Session and re-render the final view the same as the first time.
  18. I like the idea behind this pattern. It seems a framework such as struts could work with this to simplify the parameter passing (especially in the case where all form items are kept in session).

    Even if a simple ID is used for the GET, I think struts would still work well -- just requiring more thought as to the ActionForm used.

    Has anyone tried something like this in Struts?
  19. I've made a simple "super action" that helps implement the pattern with Struts.

    For this to work, some information must be stored in the Session for the GET to work.
    A simple "id" could be passed along the redirect, if it exists.

    My PRGAction below:
    -------------------
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import org.apache.struts.action.Action;
    import org.apache.struts.action.ActionForm;
    import org.apache.struts.action.ActionForward;
    import org.apache.struts.action.ActionMapping;

    /**
     * @author Jesper Udby
     */
    public abstract class PRGAction extends Action {
    /**
    * Handles the GET method.
    * Returns an ActionForward - the JSP/whatever to display in response to the GET
    */
    public abstract ActionForward executeGet(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws Exception;

    /**
    * Handles the POST method.
    * Returns an ActionForward - the JSP/whatever to display in response to the POST.
    * Return null to have the action redirect to itself...
    */
    public abstract ActionForward executePost(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response) throws Exception;

    /**
    * @see org.apache.struts.action.Action#execute(ActionMapping, ActionForm, HttpServletRequest, HttpServletResponse)
    */
    public final ActionForward executeAction(ActionMapping mapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response)
    throws Exception {

    ActionForward ret = null;
    if ("GET".equals(request.getMethod())) {
    ret = executeGet(mapping, actionForm, request, response);
    }
    else if ("POST".equals(request.getMethod())) {
    ret = executePost(mapping, actionForm, request, response);
    if (ret == null) {
    String me = request.getServletPath();
    response.sendRedirect(me);
    }
    }
    return ret;
    }
    }
  20. Tokens are clearly the solution[ Go to top ]

    While this solution might be nice to avoid the "resend information" nag message to clients, it really isn't a solution to the problem it poses to MVC.

    All forms that cannot be submitted more than once for data integrity reasons should use TOKENS. You simply create a token, tuck it in the session and pass it to the view, then when the form submits, it checks to make sure the two values are equivalent, clears the token, saves the data and forwards to the view. If back or refresh are issued, the token will no longer be valid until the user goes back in the front door, and you are safe from the reprocessing.

    Even better, when the refresh is issued, instead of showing the results page again, you can interpret the request as the user wanting to do something new and you take them to the next logical page.
  21. Another question about form resubmit[ Go to top ]


    > All forms that cannot be submitted more than once for data integrity reasons should use TOKENS.

    I am working with website in which a user can login and then submit different forms. I want the user to avoid submitting the same form twice by using the browser refresh or back buttons. I also want to give user the ability to resubmit the forms with a different data. If TOKENS are used, the user can submit one form only once.

    Is there a suitable solution where a form with same data is submitted only once, but there is no such restriction with different data? I do not want to use redirection after POST, as there are large number of different forms and then each will have to be redirected to a seperate page. (I dont think storing ALL the information filled in each form during that session and then comparing for resubmit is a clean solution).
  22. Tokens are clearly the solution[ Go to top ]

    While this solution might be nice to avoid the "resend information" nag message to clients, it really isn't a solution to the problem it poses to MVC.

    All forms that cannot be submitted more than once for data integrity reasons should use TOKENS...
    Do you have some example code of how you do this? I am new to MVC pattern.
  23. Going one step further[ Go to top ]

    in one of my last projects we used the same pattern and optimized it for back button usage: my problem was, that users use the back button and so often sees outdated data, e.g. a person's address, that has been changed in the meantime (by this user or someone else);
    solution: we set the http-headers to no-cache, so clicking the back-button always reloads the page; because this page was retrieved via the redirect (PRG) it will fetch e.g. the actual person-data (http://...../person.view?id=xy)
    but for some pages this behaviour is bad: if the user submits a search page, and then clicks back button, the search page should NOT be reloaded, because fields would then be empty and he had to type in everything again, but most often he just wanted to modify his last search, so the previously entered values should be displayed;
    so we decided to set the no-cache headers dependent on the content of the resulting page:
    search results & detail-views are always reloaded, all others may be cached by the browser; the decision whether to cache or not is best expressed as a custom tag in the jsp.

    fusel.
  24. redirect in struts[ Go to top ]

    Uhh? Like this you complete ignore the forward mechanism struts offers you!

    SIMPLY DEFINE THE FORWARD TAG WITH REDIRECT="TRUE"!

    map.findForward("myRedirectedForward");

    But take care, as discussed, you loose your current request...
  25. struts redirect=true doesnt do it[ Go to top ]

    I tried doing redirect="true" in the action forwards. It does not work. I eventually ended up using tokens and it did the job for me!

    To give a context:

    To test redirect, I used it for the login action, where the user enters credentials, and is then taken to an action as POST (something like login.do), which, upon success, forwards to something like an index_postlogin.jsp. The browser shows http://url/login.do, with postlogin.jsp rendered as the page. If I click refresh, it relogs the user (I do see the browser warning about the POST resubmit). I used redirect="true" but didnt solve the problem.
  26. Scalability benefits of PRG[ Go to top ]

    This pattern can also be used to utilize the HTTP 1.1 caching correctly. Referring to a previous example:

    showItem.jsp?id=#

    If you got to this page through a POST without a redirect then the page will never be cached on the app server/web server, reverse proxy, proxy, or client layers.

    By using a redirect and GET you can fully utilize HTTP 1.1 caching policys in the way they should be used. For example, if your items only change once a day you can set the Expires header for showItem.jsp (See ftp://ftp.isi.edu/in-notes/rfc2616.txt section 14.21). Then the results of showItem.jsp?id=# will be cached at each layer of the HTTP REST architecture. That means in effect that the JSP (and database) will only be called once per day for each item id accessed (depending of course on the resources available for caching at all layers in the architecture).

    The scalability benefits of fully utilizing HTTP 1.1 far outweigh any additional overhead created by the redirect.
  27. I'm glad people finally got talking about elementary patterns of Web applications. I propose restricting this pattern to its true form: a POST-request followed by a 303-response. These ideas are not mine, they are directly copied from "the manual" (RFC 2616) and the pattern itself is built into the Web architecture.
    You should never use GET for "sending data" to the server. This is specified in RFC 2616 and it is the principle difference between GET and POST. GET is a safe method and it should not perform any user-responsible transactions on the server's business data. The "Information must be resend" message is just a consequence of this rule.
    No specific redirect code is mentioned anywhere on this page, but I suppose you would use "the default" 302 (Found). The redirect response code should be 303 (See Other). The RFC 2616 definition of the code directly refers to this pattern. The good deal of browsers popular today misinterprets 302 as if it were a 303, so in practice you may find that it amounts to the same thing. However, I believe that it pays off sticking to the "clean" code 303, since the client with strict interpretation of the specification will respond to a 302 response with a repeated POST request to the new URI, a very bad turn of the events. 303 is properly interpreted by all modern HTTP clients, so you should not expect serious problems by using it.
    The problem with the token approach is that it breaks the Web architecture. Masking session-control data as resource identifier, avoiding idempotency of GET, blurring the responsibilities of resources and other tricks to get the protocol to work against its design is just bad engineering to me. I believe all these problems can be solved by working within the Web architecture, not for the sake of the architecture itself, but for the sake of the Web developers.
    I believe that 303-response is the best response to the POST-request, in every situation. It's too bad that it requires two HTTP-exchanges instead of one, but as Matt Pouttu-Clarke noted, it actually has less overhead then a POST-200 exchange.
  28. What is bad, redirect clear all request data. What can I do, if I need to put some/all data from previous request, or add additional data and put them into next request?
  29. What is bad, redirect clear all request data. What can I do, if I need to put some/all data from previous request, or add additional data and put them into next request?
    You have two choices: either temporary save it on the server, say in the HttpSession, or stick it in the destination URL of redirect response. The latter is not really a solution because it ruins the whole idea of refreshing result page using just object ID. So, my anwer is: store all business data on the server, and set up object ID only in the destination URL. See the "Using PRG pattern" article: http://www.theserverside.com/articles/article.tss?l=RedirectAfterPost
  30. I just wanted to comment that previous to Michael Jouravlev's PRG pattern posting 100% of all Java web applications I had worked on or seen were constructed in violation of PRG, by using POST to display content. Before HTTP 1.1 connection keep alive came around implementing PRG would have involved a new TCP/IP connection for each redirect; so maybe it is understandable that the best practice used to be using one HTTP request/response cycle to update business state AND display the next page.

    As it stands, I believe that the emergence of this pattern shows how critical it is for web application architects and developers to pay attention to the underlying protocols, not just the APIs and file formats. Innovations in the protocols can and should result in fundamental changes not only in infrastructure tools but in the web applications themselves. (Maybe some of the .NET geeks can comment on the impact of the PRG pattern on the fundamental design of ASP.NET "stateless" sessioning using hidden fields, which requires a form POST for every page transition)

    I just got off a gig as Senior Consulting Architect for DaimlerChrysler B2C (NAFTA). One of the things I did there was implement PRG as a legally binding standard for all B2C HTTP applications. These standards are written into all vendor’s contracts so they are heavily scrutinized by many very talented and experienced people including major IT consulting vendors such as IBM and Compuware.

    We went though all the discussions on this pattern POSTing and then some. What we came up with is that a web application never has to use POST to display content except in the case of a system generated error which the user should be allowed to resubmit using the browser reload function. For example, a page (with a 500 status code) saying "Your transaction failed due to <whatever>, please click 'Refresh' to try again"
  31. pb with PRG : back button[ Go to top ]

    I am using this pattern, and it works quite fine but I have a problem with the back button : it goes 2 pages back instead of one. It is the same problem with any browser.
    The test is :
    - I enter the site. It's a form page.
    - I submit the form, the server proceed a redirect to a second form,
    - I submit the second form. There 's another redirect to a third page.
    - I press the back button
    Then, the page is the first form !

    Anybody has an idea ?
  32. pb with PRG : back button[ Go to top ]

    I am using this pattern, and it works quite fine but I have a problem with the back button : it goes 2 pages back instead of one. It is the same problem with any browser.The test is :- I enter the site. It's a form page.- I submit the form, the server proceed a redirect to a second form,- I submit the second form. There 's another redirect to a third page.- I press the back buttonThen, the page is the first form !Anybody has an idea ?
    Do your second and third form have the same location? I mean, exactly the same, base URL and names and number of parameters? What may happen is that you display second and third pages from the same location, from the same controller or action or whatever it is. If this is true, most browsers would consider these two pages as one resource.

    Actually, this is the neat trick that can be used to create single-page components, which do not allow you to go to the previous page simply because all pages are served from the same location. For example, see this sample. Both pages of login component are served from one location, so you cannot go back to the previous page. This is intended behavior.

    By the way, try Opera, it usually caches every single page instead of every resource. But most other browsers do not behave like Opera, and use resource locations for page history. If locations are the same, they consider the resources are the same too.

    Michael.
  33. Hi.

    This has been around since at least 2001.

    http://www.catstep.de/zobel/post2redirect.html

    Note that the page has not been maintained since, so it is a bit outdated. Nice to see it find wider acceptance.
  34. Relating the "Post" in the post/get/redirect pattern, I would remark that the real point isn't that the method actually executed was "post" or "get", nor that any data was sent to the server.

    The important point is that the "post" is really an action, that executes changes in your server, no matter the method is post or get, no matter any data was sent. Therefore it isn't idempotent, and cannot be repeatedly executed without unpredictable results.
  35. 1.JSP form must have a parameter: <!-- Remember system time. Use this parameter to avoid dupplicate requests--> You Struts action should have one method ********************************************************************************************* * Return true if this request is due to browser reload button effect *********************************************************************************************/ public boolean isRepeated(final HttpServletRequest request) { // This flag is used to avoid recurring calling final String this_request_time = request.getParameter("request_time"); final String last_request_time = (String)request.getSession().getAttribute("last_request_time"); if (this_request_time==null) {//page does not need validation return false; } else { //Time of the last request should be stored in session to avoid recurring calls request.getSession().setAttribute("last_request_time",this_request_time); if (last_request_time==null) {//page does not need validation return false; } else {//If page needs validation then check that time of the last request is not the same as time of this request return last_request_time.equalsIgnoreCase(this_request_time); } } } Inside execute action you should put in the very beginning if (isRepeated(request)) {//This is the same call, that have already been processed. Don't do anything return mapping.findForward("success"); } You are all set !
  36. One more supporter[ Go to top ]

    Michael, Thanks for this post. I've tried to sell this solution to 2 big projects, and was not successful on both. Your work in this area will hopefully help with the next one. I'm working mostly in the ASP.NET space recently. Server.Transfer() a.k.a. "forward" is causing the POST/FORWARD issue to be more prevelant in ASP. I have posted a little write-up of my own on this topic: http://www.strong-point.com/Blogs/tabid/87/EntryID/8/Default.aspx
  37. The scenario is GET1 -> POST2:GET2 where ':' indicates redirection. GET1 creates a new model required for rendering the view. The view is a form which on submission binds the request parameters to the model. These parameters are used by GET2 for rendering the view. Now when I do a Back 'GET1' gets fired which renders the previous view. But when I click on forward since the model has changed by GET1, the request goes to GET2 and since it does not have the parameters bound in 'POST2' and hence it crashes. Kind Regards, Jeetesh
  38. GET requires some parameters for creating model while rendering view. In a POST:GET scenario POST can provide those parameters thru some session object. But in that case I cannot reuse GET in isolation. So what is the best practice for avoiding the above limitation ? Kind Regards, Jeetesh