Is there a standard or accepted way to recover from a page refresh in an Ajax web application?
My interest now lies mainly in a web app with a Java (JSP/Servlet) Back end. My page is initially rendered from a JSP and then the user progressed through the interface using javascript events.
Is there a design pattern which covers this, I'm assuming that the refresh button is someting that web developers need to worry about quite often so there should be a way of recovering from it, while maintaining state.
There are a number of way to handle this.
Anchors - This is what Gmail does when it tacks on #inbox/123 which means that it should show the email id 123 with the label inbox. This is not very expressive and is useful for simple states. However, it does provide users the ability to bookmark the page and use navigate through browser history.
Cookies - This has the advantage that this can be managed entirely on the client side. You can set cookies via Javascript and restore them via Javascript. It's cheap and doesn't require and post backs. The state information usually doesn't need to be persisted on the server because typically the state is temporary.
Sessions - This will need you to post back the state information back to the server via AJAX as the client updates the page. If the client refreshes the page, the new page incorporates the changed state into the newly rendered page. This is quite costly in terms of performance and also complicates design but may be useful for certain applications.
My suggestion would be keeping a state machine for each user on server side which changes states with the AJAX calls. That way on the refresh, you'll already know in which state position the user was in, allowing you to recover from this.
This might bring you a problem with scalability if you are not careful while coding it.
Another solution might be storing the state variable in the cookie (assuming the user has active cookies). And on page load, the state variable would be submitted to your web application, allowing you to recover.
Here's one solution we used in a project:
You assign a sequence number / random guid to the page eash time the user visits the page. You can put that into the get part of the url (such as yourpage.jsp?pid=1337 where 1337 is the page view sequence number)
When you process the AJAX requests, you maintain a "mirror" of the page state on the server side in the session or whatever mechanism you can use in JSP to store state.
When the user requests the page, you check if the given sequence number already exists, and if it does, it means that it's a refresh so you render the page using the mirror data you have in your session.
Related
I have a SPA website set up with a REST API. One of the issues I have come up against is how to access data unrelated to the resource being requested. Specifically, accessing the users account details so I can do things like hide admin actions when the user is not an admin.
I have an api endpoint that can get all this data so the simplest solution would be to on every page load request the profile api as well as the resource actually being accessed. This seems a little wasteful to be constantly requesting a resource that rarely changes.
Another option would be to store this data in local storage so it only has to be requested once but then I have the issue where the user updates their username or settings on one device and then the other one is left with outdated data in local storage.
I thought vuex might be able to persist this data when clicking on links and navigating to different pages but it seems like the data gets lost when the page changes.
From what I understand graphql would solve this problem by allowing the initial request to get the user data along with the other data. I'm not really sure how much more efficient this is than 2 requests and rewriting my whole api probably isn't the best solution to this.
Are there any well known solutions to this problem or is one of the options I have come up with the best way to handle this?
Background:
I have a single page application that pulls data from a REST API. The API is designed such that the only URL necessary is the API root, ie https://example.com/api which provides URLs for other resources so that the client doesn't need to have any knowledge of how they are constructed.
API Design
The API has three main classes of data:
Module: Top level container
Category: A sub-container in a specific module
Resource: An item in a category
SPA Design
The app consuming the API has views for listing modules, viewing a particular module's details, and viewing a particular resource. The way the app works is it keeps all loaded data in a store. This store is persistent until the page is closed/refreshed.
The Problem:
My question is, if the user has navigated to a resource's detail view (example.com/resources/1/) and then they refresh the page, how do I load that particular resource without knowing its URL for the API?
Potential Solutions:
Hardcode URLs
Hardcoding the URLs would be fairly straightforward since I control both the API and the client, but I would really prefer to stick to a self describing API where the client doesn't need to know about the URLs.
Recursive Fetch
I could fetch the data recursively. For example, if the user requests a Resource with a particular ID, I could perform the following steps.
Fetch all the modules.
For each module, fetch its categories
Find the category that contains the requested resource and fetch the requested resource's details.
My concern with this is that I would be making a lot of unnecessary requests. If we have 100 modules but the user is only ever going to view 1 of them, we still make 100 requests to get the categories in each module.
Descriptive URLs
If I nested URLs like example.com/modules/123/categories/456/resources/789/, then I could do 3 simple lookups since I could avoid searching through the received data. The issue with this approach is that the URLs quickly become unwieldy, especially if I also wanted to include a slug for each resource. However, since this approach allows me to avoid hardcoding URLs and avoid making unnecessary network requests, it is currently my preferred option.
Notes:
I control both the client application and the API, so I can make changes in either place.
I am open to redesigning the API if necessary
Any ideas for how to address this issue would by greatly appreciated.
Expanding on my comment in an answer.
I think this is a very common problem and one I've struggled with myself. I don't think Nicholas Shanks's answer truly solves this.
This section in particular I take some issues with:
The user reloading example.com/resources/1/ is simply re-affirming the current application state, and the client does not need to do any API traversal to get back here.
Your client application should know the current URL, but that URL is saved on the client machine (in RAM, or disk cache, or a history file, etc.)
The implication I take from this, is that urls on your application are only valid for the life-time of the history file or disk cache, and cannot be shared with other users.
If that is good enough for your use-case, then this is probably the simplest, but I feel that there's a lot of cases where this is not true. The most obvious one indeed being the ability to share urls from the frontend-application.
To solve this, I would sum the issue up as:
You need to be able to statelessly map a url from a frontend to an API
The simplest, but incorrect way might simply be to map a API url such as:
http://api.example.org/resources/1
Directly to url such as:
http://frontend.example.org/resources/1
The issue I have with this, is that there's an implication that /resource/1 is taken from the frontend url and just added on to the api url. This is not something we're supposed to do, because it means we can't really evolve this api. If the server decides to link to a different server for example, the urls break.
Another option is that you generate uris such as:
http://frontend.example.org/http://api.example.org/resources/1
http://frontend.example.org/?uri=http://api.example.org/resources/1
I personally don't think this is too crazy. It does mean that the frontend needs to be able to load that uri and figure out what 'view' to load for the backend uri.
A third possibility is that you add another api that can:
Generate short strings that the frontend can use as unique ids (http://frontend.example.org/[short-string])
This api would return some document to the frontend that informs what view to load and what the (last known) API uri was.
None of these ideas sound super great to me. I want a better solution to this problem, but these are things I came up with as I was contemplating this.
Super curious if there's better ideas out there!
The current URL that the user is viewing, and the steps it took to get to the current place, are both application state (in the HATEOAS sense).
The user reloading example.com/resources/1/ is simply re-affirming the current application state, and the client does not need to do any API traversal to get back here.
Your client application should know the current URL, but that URL is saved on the client machine (in RAM, or disk cache, or a history file, etc.)
The starting point of the API is (well, can be) compiled-in to your client. Commpiled-in URLs are what couple the client to the server, not URLs that the user has visited during use of the client, including the current URL.
Your question, "For example, if the user requests a Resource with a particular ID", indicates that you have not grasped the decoupling that HATEOAS provides.
The user NEVER asks for a resource with such-and-such an ID. The user can click a link to get a query form, and then the server provides a form that generates requests to /collection/{id}. (In HTML, this is only possible for query strings, not path components, but other hypermedia formats don't have this limitation).
When the user submits the form with the ID number in the field, the client can build the request URL from the data supplied by the server+user.
I am using the graph api to get data about pages and the posts in the pages.
When a post is published, it gets liked, commented upon and shared over time. When I read the data next time how can I get the posts that have those changes alone?
the best way is really to set up a server to receive real time updates. Any other way would mean polling facebook endpoints. At a certain point, a single user access token would be rate limited, and would block you from making a call for a certain amount of time. Also, there would be more work to compare each post to the one you stored to see if anything has changed.
Really the most efficient way is to use real time updates in which you set up an endpoint on your server to receive messages from facebook whenever something on a page (or user) has changed. If cost of keeping a server running is your roadblock, I would recommend to setup a free Parse.com account in which you can set up a server to handle Facebook's incoming requests and act on that.
I hope that makes sense! More information on realtime updates here: https://developers.facebook.com/docs/graph-api/real-time-updates/v2.2
I know that to make a stateless application, we need to transfer the user state back and forth instead of server holds the user state.
However, there must be some states stored in the server, I read this article that the state stored in server is called resource state.So if I am right, client state which we often call should be the same as application state.
So, how do I distinguish these two, since it will determine that whether they should stored in server or transferred.
Take a shopping cart as an example.
If there is 5 steps before a user to complete his purchase, the user's phase where he is(#3,#4) in seems to be an application state, but does this mean if they close the browser and click on pay again, he will have to start from the step1?
What about the items in his chart? If we treat it as application state, we need to put all of the items in the request. But if we do this, when user close the browser and login again, he will not able to find his items again, since the browser can not remember all the items. So it seems we should treat it as a resource state. But if so, when a user click on pay , they will have a different page: go to pay or say "your cart is empty" based on whether his shopping cart is empty or not. So , the same requests with exactly the same param input, comes out the different result, can we still say it is stateless?
Maybe I understand something wrong, can any body please answer how to distinguish different kinds of state and how to treat them differently?
Resource state is a state that needs to be persistent and survivable even after client disconnect/restart/session end/whatever.
Application state should live on the client and should be supplied with each client request(if we are talking about REST architecture and planning to scale our application well).
How to distinguish application state and resource state?
It depends on the task your are working on. E.g. if you are trying to figure out where to save index of the picture that is currently viewed in your gallery, probably, you could do it in your Application state, because you, likely, don't need this state to survive for the next session of this client. Of course, you can save it in your Resource state(database), but it would be overhead(a lot of effort for a very small gain).
But if your are working on multistep purchasing process, probably, it's better to save state of this process in your Resource state(database), because you want this state to be saved permanently. Otherwise, your clients need to refill a lot of information after disconnect/restart/whatever.
Of course you could do it in cookies for example(and it would be Application state), and this state can live after browser restart. But it has the two downsides: 1)This state unavailable on other user's devices, 2)If you are creating genuine REST service, cookies would complicate client's life, because not all clients operate cookies well(except browsers).
Let me just quote one paragraph of the book RESTful Web Services :
The Flickr
web service lets you upload pictures to your account, and those pictures are stored on
the server. It would be crazy to make the client send every one of its pictures along with
every request to flickr.com, just to keep the server from having to store any state
The application state is related to the path that the client can follow to make some actions.
For example, when consulting an article, a link "add to cart" appears. When consulting his cart, a link "pay your order" is provided if you have one article in your cart otherwise this link does not appears. Feel free to the user to make its own application state based on the link that he follows. Basically, application state is a matter of context.
One other quote from the same book mentionned earlier before I go back to you example:
Resource state stays on the server and is
only sent to the client in the form of representations. Application state stays on the
client until it can be used to create, modify, or delete a resource. Then it’s sent to the
server as part of a POST, PUT, or DELETE request, and becomes resource state.
So let's say that you have some authentification mechanism (based on token). And to one user account is associated one cart.
When you are adding items in your cart, you are modifying your cart resource. As resource state are server-side, it's on server.
Suppose that you disconnect, and reconnect like described in your first point. The cart is still here.
As long as the client send the different authentification credential at each request, your application remains stateless.
A good discussion on SO about how to manage it: Do sessions really violate RESTfulness?
Now, what about the fact that: consulting your cart can leads you to 2 different action depending on whether it has items or not.
Pretty simple. What is served by the server to the client depends on the resource state maintains by the server.
A very simple example on this good website. You can see that depending on the amount of money on the account, the server provides a link to the client to make a withdraw or not.
Feel free to the client to make its own application (again) and to follow the link or not.
I recommand you to take a look at HATEOAS and the Richardson Maturity Model that explains it.
By the way, the quotes from the 2 paragraphs are from the same author that this model.
My objective is to redirect the user to an external site and have them come back to my site but preserve the Trinidad pageFlowScope. This way I can do some processing and state saving in various pages, send them off to the external site for some external processing and then have them come back to my site to finish processing. From the users perspective they'd appear to be doing one continuous flow and possibly be doing multiple of these concurrently in different browser tabs.
The way I'm trying to transition everything, which does work fine except state is not restored, is by doing a post from a page in my flow to the external site and then the external site does a post back to my site in the next page. It's almost like a disconnected ADF task flow executing on a separate server and the returning back into my task flow.
mysite.foo/faces/summary.jsf -> POST externalappserver.foo/faces/startexternal.jsf -> another page -> POST myite/foo/faces/continue.jsf
I've looked all over the place and have found various issues surrounding javax.faces.ViewState but nothing on pulling that out, giving it to an external site, and then having an external site post it back some time later to a different (or even the same) page where it came from originally.
Any ideas on how I can manipulate the state so that I can rendezvous back to my site properly?
I ended up solving this by putting the Trinidad hidden form parameter on the callback URL I passed to the other service. So if my server/app is http://localhost:7001/context/pageout.jsf and it posts to another site with the callback URL, the call back URL is now:
http://localhost:7001/context/pageout.jsf?_afPfm=234z345
This allows Trinidad to restore it's page flow in conjunction with the JSESSIONID cookie provided by the browser. I didn't need to deal with the ViewState field or any other session problem. My pageFlowScope is restored all is well.