So here is my situation.
I have jsp A that does a query and displays data. On jsp A I have a form which will POST to a new servlet, the action on the form is "/data" or too the servlet directly. The doPost method will forward the request to a new jsp (jsp B) I am working on. This jsp B allows the user to select a second data point and, on submit, will forward two data sets back to jsp B where the data sets are presented side by side for comparison.
Now jsp B allows the user to drill down several layers of increasing detail. Each drill down makes a new POST back to the servlet which, in turn, forwards the new data back to jsp B. Again, the form on jsp B has an action of "/data", the servlet itself.
So goes to jsp B does a comparison and drills down two levels. Now the user wants to go back up on level. If the user clicks the browser "Back" button, the browser displays an error. A refresh of the page then displays the proper level of data.
I want the back button to automatically go back a level, resubmitting the form if necessary, and displaying the proper data.
In researching this, I found mentions of Post/Redirect/Get patterns. I also found this SO question and answer indicating that once a post is made that redirect should be used in lieu of forward.
How do I achieve the desired result without forcing the user to a) start the comparison over, and b) not have to hit refresh after the the back button.
Thank you
Related
This question already has answers here:
Design Patterns web based applications [closed]
(5 answers)
Closed 6 years ago.
EDIT: I have posted a somewhat shorter and revised question here: Java web development: transfer control from one servlet to another while passing the request object (Version 2)
As more or less a beginner at Java web development, I’m unsure about how I should structure the flow between servlets/pages when a form is submitted (POST). It’s an elementary issue, I suspect this may be an easy question to answer for the experts. (Still, my book and some googling didn’t deliver a clear answer.) My question is a bit long, and that's because I want to make it clear where I'm coming from. Thanks for you patience.
Let’s say we have two servlets A en B, with each having its ‘own’ .jsp-page; let’s call those pages a.jsp and b.jsp respectively. Now as long as there are no forms on either page (i.e., no POST method used), it’s clear how things should go. That is, before any .jsp-page is shown, the corresponding servlet is activated, doing some preparation for the .jsp-page by setting the relevant data elements (most notably, as attributes of the request object) that the .jsp-page needs, then forwarding the request object (etc.) to the .jsp-page, which then actually displays the page with the data. So for example, a link on page a.jsp may link to the servlet B, and on clicking that link a GET-request for servlet B is triggered, which then does some preparation (setting some request attributes), before forwarding to its ‘own’ .jsp-page (i.e. b.jsp).
But now let’s assume that page a.jsp displays a form with a submit button, method=”POST” and action=”B”. Then yes, servlet B is activated, and this servlet has to determine whether the data entered by the user is valid. If the data is in fact valid, we can simply forward to b.jsp, no problem there. But what if the data is NOT valid?
In that case, we obviously want to show a.jsp (the form page) again, with the data that the user entered the first time still present. One way to achieve this, is to simply have servlet B forward to a.jsp (thus bypassing servlet A). However, there is a big problem with that: the URL shown to the user, in the address bar, will still read “……/B”. So the user will see the correct page (i.e., a.jsp, containing the form), but with the wrong URL (/B). So for example, if we take “Register” and “ThanksForRegistering” instead of “A” and “B”, the user will see register.jsp – but with URL “……/ThanksForRegistering”! Not good.
And calling ‘include()’ instead of ‘forward()’ on the request-dispatcher doesn’t seem to work either. If we do that, not only does it result in a GET-request (as opposed to the POST-request we want), but we actually lose the whole (original) request-object with its attributes (which we need, after all, to re-populate the form). At least, that’s what my own experimentation seems to show. So using ‘include()’ doesn’t seem like a viable option at all.
Another obvious idea is to have "action=A" (instead of "action=B") for the submit. Then the servlet A itself can handle the validation, and if validation fails it can simply forward to a.jsp again, no problem. BUT then what if validation succeeds? Then we want to show the follow-up page b.jsp, but that page may well need the attributes from the original request-object (from the form-submit) again; for example, to have the user check that his entered data was in fact all correct. So basically we have the same problem as before, but with the roles of A and B (and their respective .jsp-pages) reversed. So this doesn't seem like a real solution either.
And I don’t see any other alternatives.
So basically, I’d simply like to be able have one servlet give control back to another servlet, but with the request object being passed from the former to the latter servlet. Or, if that’s not possible, I’d want to be able to forward from servlet B to a.jsp directly, but with the correct URL shown to the user. Or any other way to accomplish what I want.
Many thanks.
I think that the assumption that there has to be one page per servlet is causing the problem here....have one servlet which based on input redirects,forwards or includes a particular page....you dont really need to always invoke a different servlet for a page.....you can have a single front controller with a view resolver the combination of which will redirect or forward to a page.
You can use filters to achieve the same thing or think of setting attributes in HttpSession if validation is successful and retrieve the data in all the pages whenever it is required.
session.setAttribute("object", object);
I hope this is what you are looking for.
The application is to ship items where on 1st page customer fills up information. Then on 2nd page he gets different rate options to choose from. Then on 3rd page he needs to fill up address info. Then on 4th page payment info & so on.
Now let's say customer enter details on the 1st page. I send ajax request to server & get different rates options.
Once I come back in ajax success, I need to change the page to 2nd jsp. Once customer chooses rate options then again make ajax request & get 3rd jsp & so on.
I need to implement UI side of the code without reloading or redirecting the page. Also from 2nd & 3rd jsps I should have a back button to go to the previous page.
Is it possible with only jquery like setting/replacing divs or is there any plugin I could use like twitter bootstrap carousel?
I need some help in ajax success function so that I can go to next or prev jsps with submit & back buttons?
I am using spring MVC framework.
I think you are a bit confused about who makes what: you are mixing jsp and ajax, server side computation and client side.
Don't care about jsp or spring. They have nothing to do with what you need: you are going to implement a single page application, so jsp and spring will only be in charge of deliver datas to the client, for example, in the form of json (to bind to a template, see jsview) or html (to append a dom element).
Customer enter details on the 1st page, on submit, an ajax call happens. The response of the call will be, as I said above, json or html that will be bind to your document, so now you can show the new data and hide the old one, with bootstrap carousel is ok... and so on for the other pages.
For the back button, if you use only html5 browser you should manipulate the browser history (https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history), if not you should use a polyfill like history.js (https://github.com/browserstate/history.js/)
This response is only a clue of a way to do what you need. AngluarJS or ember.js are another keyword you can use to search solutions to your problem.
I am developing a spring MVC application. I ran into some interesting case.
To make it easier to explain i am taking the stackover flow buttons on the top as example( i mean those questions, tags, users, badges, unanswered buttons).
Now in my app i have similar buttons. when user clicks on any button it makes ajax call by passing proper arguments. Server makes sql queries and returns the results back.
Now assume that there is a crazy user like me who keeps on clicking those buttons without break. So each click is making a ajax call. And which ever completes its operation is showing up on front end. So even if the user clicks Tags button in the last it may show up and again the previous click on questions which took long time to return to front end can overwrite the page. How can i fix that? ( i want the tags data to be shown as it is the users last click)
In the first place i know that when user first clicks on question and then on tag i no longer need to query sql for questions button. Is there some way for me to stop processing the sql query for questions button.
Thanks
The best way to handle this is through the user interface - if the user takes some action (clicking an image) that will require significant processing on the backend, your UI should prevent other actions on the page from sending further messages to the backend until the original request is complete.
Ways to tackle this visually would be to disable/gray out other elements, make it obvious that some work is going on (with spinners, progress bars), etc.
On the server side, since each HTTP request is independent it would be cumbersome and difficult to add logic on the server to be able to detect if the user making this current request has another ongoing request currently being processed.
You probably need to take help of cookies. When the first time the action is done, write some cookie. Every time, check that cookie before you process.
You cannot simply disable a link or button from the UI and hope the user cannot do it. It can always be done in multiple ways. Additional checking is must.
(I haven't read your post completely. But from what I understand from the 1st answer...)
I had a similar problem, and I tackled it this way.
I did hand-coded ajax calls (as opposed to jQuery etc.)
I had a single global XMLHTTPRequest.
var xhr = new XMLHTTPRequest();
When the user clicked something, which needed an ajax call, I aborted the previous call, if already running.
if( xhr.readystate !=0 || xhr.readystate !=4 )
xhr.abort();
Then create a new instance of XHR, and do your business.
xhr = new XMLHTTPRequest();
xhr.open("GET", myUrl, true);
//attach callback function etc and do the send
I have a following question. We have an application that is a basically a set of JSPs and action classes that control the interaction with the user. The actions set up some parameters required for the page to display properly. The urls in the system always contain the name of the event which defines which page we should go next.
Now, almost every page has a Cancel button that should lead to the previous page. From the start each cancel button url was strictly defined, but eventually, as the system grew larger and larger, it turned out to be quite hard to program the logic for this button so that it would lead exactly to the previous page.
For example, suppose we have three pages, A, B and C. Page A has a link leading to page B, and B has a link leading to C. Therefore C has an url with even leading to B (and also it contains the key of the entity to display on B). However, suppose page A was modified and now also has a link on C. Now we need to check on C page from there we came and set the URL appropriately, and the logic becomes pretty mixed.
So, I proposed to my team the following solution. The user session should contain a special object called the CancelStack. Every action, that leads to the page, should push it's url inside the stack (containing the event and some additional data that is required). On every page the cancel button should now have an url leading to a special event, called cancelStack.
What the cancelStack action does is this:
Retrieve the cancel stack from the session.
Pop the last url and DO NOT use it.
Pop the url again and redirect to that URL.
Why do we retrieve the last url without using? Suppose we have pages A and B, A leading to B. Action for A places it's url inside the stack, and this should be the cancel url for the B page. Now, action for B places it's url inside the stack. Therefore I pop it without using, and then pop the first url, redirect to A action, and this action adds the A url again to the stack (therefore stack size decreases only by 1, not by 2).
It seems like a pretty good scheme, however it seems rather strange that the top element of stack is popped without using. Therefore I have a question. Is there any design patter in order to store the sequence of URLs inside the session in order to organise cancel buttons properly?
What you did seems reasonable to me. To be honest I can't think of a design pattern addressing your issue right now, but I think if besides the cancelStack you keep a reference to the currentURL so that you push the currentURL to the stack only if you don't end up in a cancel page you will get rid of the extra pop that annoys you.
Otherwise you just pop the top of cancelStack.
E.g. in your example:
Suppose we have pages A and B, A leading to B...
currentUrl is A. Action for A is not cancel so currentUrl i.e. A is pushed to the cancelStack. Then it is action for B and currentURL is B but action is cancel so B is not placed in the stack. So the top of cancelStack is A (while currentUrl is B). So if you pop the cancelStack you retrieve A (no extra pop needed)
A good design solution when a form is submitted, what should be the behavior of "back" and then "forward" browser button.
Similar question is what should happen when a user logout an application the then click "forward" browser button?
I will be glad to hear some scenarios for the mentioned situations.
Thanks.
Edit - should be good to share and my point of view :-)
My personal opinion is after logout the user should be not able to enter the application without go through the login page.
For the submit scenario - after submitted and back browser button , the user should be able to return to the form but with NO containing data.
One common pattern is Post/Redirect/Get. Under that pattern, the result of the post is a bookmarkable (and back/forward navigable) page. The Back button has one of it usual meanings of "I didn't mean to go here, take me back where I was" like hitting ESC in most Windows dialogs, and the Forward button means "I didn't mean to hit the back button, I wanted that page after all." This pattern isn't going to work for everyone; it makes the most sense when each page (including the response to a form submit) represents some conceptual entity that you'd want to bookmark.
As for the logout scenario, most apps check whether you're logged in no matter what page is specified in the URL, and redirect to the login form if you're not logged in. (You don't have to code that on every page; the check is usually a Valve or something.) A nice feature is to remember where the user was trying to go and take them there upon sucessful login.
Your question is more about design than technology, so GWT doesn't really change the picture, except to note that the GWT history mechanism is intended to mimic the behavior of static pages connected by links, which the post/redirect/get pattern does also.
It is very common to use state machines to keep the user(session) and request state. If you have such a state machine then you know that user is trying a wrong transition. Depending on the user state and the wrong transition you can forward the user to a page. For example if the user tries to go to a page which needs to be logged in but she/he has already logged out, you can send her/him to a login page but you can provide user name and only ask for the password.
To add this functionality you can write your own code by hard-coding the state machine in your code or you can use one of the available libraries. For example,
Spring Web Flow provides this functionality for Spring framework.