How to uniquely identify each new browser tab/window? - asp.net-mvc

I am trying to implement the idea found here in order "To facilitate multi-tab session states"
The shared solution speaks of Web Forms and wondering if it is possible to get a unique id for each new tab in asp.net MVC in order to use for each instance of session variables.

You won't be able to reliably check if a user has closed a tab in MVC due to the nature of restful Http requests.
If it's absolutely essential to keep track of open tabs I would think about SignalR, you can reliably track open tabs in real time using that. You would have to implement a collection of unique session objects synced to a unique ID on the matching tab. On submitting the form you would match the session by ID to the ID passed in by the form.
Having said that I would be very cautious about keeping anything specific to a certain page/tab in a session. It can lead to many confusing bugs that are very hard to debug, often leading to complete reworks.

Related

MVC beginner - Advice on how to persist a user selection for use throughout the site

I have an application which is centered around data per 'team'. A user belongs to a team and if they log in they only see that team's data.
However, I now have super users who essentially belong to more than one team. These users should be able to log in to the system and then immediately choose which team they are interested in. From then on they will essentially view/create data against that selected team. They should also have the option to go and change what team they are viewing at any time.
I've established that the user would like to be able to have multiple tabs open and be viewing a different team in each tab.
I'm struggling to work out the best way to accomplish this with .NET MVC while keeping it as stateless and testable as possible.
I've been reading up on the different ways to persist data - session state and cookies seem to get a bad rep in MVC. TempData, ViewBag seem to focus on just persisting data for one request.
I wouldn't have thought that this is an uncommon requirement in an application - are there known patterns for dealing with this in MVC which I have missed?
So far I'm trying to create a partial view which I can show on each page to let the user see what team they are viewing the site as, and change it from there.
Any advice is appreciated!
If you want to let your superuser view multiple teams data then you'll want to pass the team information in on the request, on the query string or as something that looks like a restful url:
/blueteam/members
In fact it would be extra work to track this in a stateful manner as you'd have track user, team, and ui element when a superuser can view multiple team data at once.
I'd say passing the information in on every request is a pretty standard approach to your situation.
The tricky part of the stateless approach is decorating all your internal application links with the team information without too much extra work. Relative links can be your friends here. So a link to the bug page for a team might be to simply "bugs", picking up the team name higher in the uri path. If you are creating something that looks like a one page application it's easy enough to store the team info on the client.
If you don't want team members to see data for other team members, you can set up guard functions that check for team membership for certain classes of users before rendering a view.

Rails - Store unique data for each open tab/window

I have an application that has different data sets depending on which company the user has currently selected (dropdown box on sidebar currently used to set a session variable).
My client has expressed a desire to have the ability to work on multiple different data sets from a single browser simultaneously. Hence, sessions no longer cut it.
Googling seems to imply get or post data along with every request is the way, which was my first guess. Is there a better/easier/rails way to achieve this?
You have a few options here, but as you point out, the session system won't work for you since it is global across all instances of the same browser.
The standard approach is to add something to the URL that identifies the context in which to execute. This could be as simple as a prefix like /companyx/users instead of /users where you're fetching the company slug and using that as a scope. Generally you do this by having a controller base class that does this work for you, then inherit from that for all other controllers that will be affected the same way.
Another approach is to move the company identifying component from the URL to the host name. This is common amongst software-as-a-service providers because it makes sharding your application much easier. Instead of myapp.com/companyx/users you'd have companyx.myapp.com/users. This has the advantage of preserving the existing URL structure, and when you have large amounts of data, you can partition your app by customer into different databases without a lot of headache.
The answer you found with tagging all the URLs using a GET token or a POST field is not going to work very well. For one, it's messy, and secondly, a site with every link being a POST is very annoying to work with as it makes navigating with the back-button or forcing a reload troublesome. The reason it has seen use is because out of the box PHP and ASP do not have support routes, so people have had to make do.
You can create a temporary database table, or use a key-value database and store all data you need in it. The uniq key can be used as a window id. Furthermore, you have to add this window id to each link. So you can receive the corresponding data for each browser tab out of the database and store it in the session, object,...
If you have an object, lets say #data, you can store it in the database using Marshal.dump and get it back with Marshal.load.

Session and Tabbed Browsing ASP.Net MVC

I'm once again looking into the world of tabbed browsing and Sessions. Looking over a few google searches it seems that there isn't a nice way of supporting this.
Does anyone know of a method that allows Bookmarking without stealing a session (cookieless) (and this doesn't work in MVC2 for dataannotations).
Supporting tabs in such a way that it's per use case (like Windows Workflow), going through two workflows at once.
I'm thinking a url in the query string might support this, but I'm wondering if anybody else has done a similar implementation.
[Edit] Use Case: Say I'm writing an application that uses something like Windows Workflow. Each UI workflow may do an action such as collect settings of a page and execute some external process. I may wish to do two of these workflows at once (not necessarily the same UI workflow). As such if I saved in session I would get:
a) Different tabs interfering with the workflow
b) Previous/Next buttons would be extremely difficult to work out, due to a).
I would like it so either, a user cannot open another tab to a url (don't think there is a 100% method of preventing this), or allow a user to use a UI workflow in isolation without one affecting another (much like running two workflows in two different browsers).
Hopefully that gives an indication of what I'm attempting to do.
Regards,
J
It sounds like you might be trying to do the following:
For example, let's say you have a two page questionaire, the first page has first name on it and the second page has last name on it. You desire that the user can open two tabs, and be at different pages in the questionaire while entering different data in the questionaire in each tab.
So in Tab A, you have entered Mark as the first name and submitted and you are at page two now in Tab A. You decide you are going to do a questionairre for your friend also, so you open up a new Tab, Tab B. In Tab B you enter Tom and submit the page.
Currently in the browser you have Tab A, which is at page 2 of the questionaire with firstname = "Mark" and Tab B which is at page 2 of the questionaire with first name = "Tom". Assuming you wanted to maintain both of these in session on the server here is an approach that i think will work for you.
When a web browser requests page 1 of your form, on a GET request(no posted questionaire data to the server), you supply a hidden field in the the response html and generate a random number to store in that field. When this form is submitted you do the following on the server:
Look in session using the random number as a key "var questionaire = session[Request.Form["questionaire_rnumber"]]
if the questionaire is not in session you create a new questionaire and update it's properties and stick it in session
var questionaire = new Questionaire();
questionaire.FirstName = Request.Form["firstName"]
session[Request.Form["questionaire_rnumber"]] = questionaire;
if the questionaire was in the session you simply update the object, and display the next page, however when you display the next page you will want to supply the hidden random number field in the html again, using the same random number you used on page 1.
This way you can hold any number of questionaires in a single session. With MVC.NET it should be straight forward for you to add the random number field to your view model and add the logic for looking in session for an existing questionaire or creating a new one and I think you'll be good to go.
You should keep in mind the possible issues with the approach also, like back button issues, security issues, and performance issues.
One example of a security and a performance issue would be that an attacker realizes your application works like this and the attacker requests page 1 of your form 10,000 times and submit the page 1 each time. You would have 10,000 questionaire objects in that one user session. If the attacker deleted his session ID cookie 10,000 times and for each session id cookie he created 10,000 requests for page 1 and submitted the page 1 form, you would have 100,000 questionaire objects cumulatively across 10,000 sessions on your server. So you should put some constraints on it also to protect your application, for example:
Any individual session can only have X questionaires in session
Any individual IP address can only have Y concurrent sessions (this you would probably need to track in the Application object)
ADDITIONAL RESPONSE TO ADDED USE CASE
Thanks for the use case. My solution should still work for you. You have two options.
If you want to ensure there is only one tab working with your workflow, then when the random number is passed to the server from a new tab you will be able to detect that there is another workflow in progress and that the random number from the new tab does not match the random number from the first, so you will throw an exception and show the user some messaging that says they can't start a new workflow until they finish the first one, and ask if they want to cancel the first. You have to ask if they want to cancel it because if they close their browser on the first workflow they started they will be stuck until their session expires. Which won't happen if they keep trying to start a new workflow.
Secondly, you could allow them to do multiple, but segment the context of each workflow by the random number, as suggested in the first answer. The whole point is that you are making little mini-sessions in your session, but keyed of a value that is only stored in the client. So since each tab has a different random number when the form posts to the server, it's easy to correlate that random number with an entry in your session that has all the information about the workflow initiated from that tab.
Hope this helps.
You need to store wizard state information in the client in some way, via query string or form values. As you've intuited, Session will not work. Nor will anything else that relies solely on what is on the server.

Asp.NET MVC Customer Application

I'm designing (and developing) web software that will allow the general public to sign up for a service, become a customer, and exchange fairly sensitive data.
I'm working through the documentation and the tutorials, and of course the RESTful pattern adopted by the default routing in ASP.NET MVC is to do URL's like this: /customer/edit/3487.
I guess I am a little squeamish about displaying such technical details as customer ID in the URL bar.
What do the smart kids do these days? Does RESTful have to mean "put your record ID's on display"?
Edit: In an ASP.NET WebForm I would have stored this in the session, I think. But I'm finding that this is discouraged in ASP.NET MVC.
Edit:
I do not intend to rely on security through obscurity.
That still doesn't mean its a good idea to give the users any ideas, or any information about the underlying data. Let's say I have an app that's publishing information about the different business in a Chamber of Commerce, to be arbitrary. Once you are logged in, you have an administrative right to click on every business in the directory and see them all - but the application is supposed to spoon feed them to you as search results or the like. Just because the user technically is allowed to access all records, this doesn't mean it should be trivial for you to write a screen scraper that downloads all of my content in a few minutes. As well, the user can just look at customer ID's and make a guess about how many customers I might have. There's lots of good reasons not to display this.
As long is there is proper authentication and authorization being done on server side then displaying ids is not an issue.
Otherwise just try to encrypt the particular id or username in the URL, this way it will be difficult for the attacks.
You don't have to put the Id in the Url, you just need to use a unique value or unique combination of values to find the data you want to display.
I'd think that the actual bussinesses name would be good and also look good in the Url. So you would have something like this:
/Business/View/theouteredge/
Or if the business name is not unique you could use a combination of business name and zip/postal code.
/Business/View/theouteredge/78665/
You would have to write a new route to handle this.
routes.MapRoute(
"Bussiness",
"Business/{Action}/{name}/{zip}/",
new { controller = "Business", action = "Index", Name = "", PostalCode = "" }
);
All this action would need to be secured with the [authorize] attribute, or the controller its self.
If you also decorate your actions with [authorise] then if another user does use the id from another user, they will automatically be challenged for a login.
It's 6 of one and 1/2 dozen of the other as to whether you use an ID or a Name. Eventually they both resolve to a record.
The important thing is to only allow authorised persons to view the data by allowing them to log in.
I've got a site which has sensitive data but only if you are the holder of that info can you see it and I do that by decorating my actions and checking rights etc.
I think that putting an ID in a url is fine -- as long as it is a Surrogate Key. The key has no value, except to identify a record. Just make sure that the requester is authorized before you send sensitive data back to the client.
Update:
I can see how having a number as part of your URL is undesirable. After all, a URL for a web app is part of the user interface, and exposing such internal details can take away from the UI's elegance. However, you are faced with limited options.
Somehow, you have to identify the resource that you want to get. The crux of REST (IMO) is that a request to a server for a particular resource must be described entirely by the request. The key for the item you want has to be encoded into the HTTP GET somehow. Your options are: put it into the URL somehow, or add it to a cookie. However, adding a key to a cookie is frowned upon.
If you look at this site you will see the question id in the url. If you view your profile you will see your username. So you would probably want to use usernames intead of an id.
If you're really concerned about it you can use a Guid, which isn't very user friendly but would be very hard to guess. :)
If you use some other way than customer id simply because you're concerned about security, then that means you're using security through obscurity, which is a bad idea. Proper authorization would require something like you either 1) have to be logged in with that customer id, or 2) be logged in as an admin, to have that request succeed.

How to pass context around in a ASP.NET MVC web app

Ok, I'm a newbie to ASP.NET web apps... and web apps in general. I'm just doing a bit of a play app for an internal tool at work.
given this tutorial...
http://www.asp.net/learn/mvc-videos/video-395.aspx
The example basically has a global tasklist.
So if I wanted to do the same thing, but now I want to maintain tasks for projects. So I now select a project and I get the task list for that project. How do I keep the context of what project I have selected as I interact with the tasks? Do I encode it into the link somehow? or do you keep it in some kind of session data? or some other way?
As it sounds like you are having multiple projects with a number of tasks each, it would be best practise to let the project be set in the URL. This would require a route such as "/projects/{project}/tasks". It follows the RESTful URL principle (i.e. the URL describes the content).
Using session state will not work if a user possibly have different projects open in multiple browser windows. Let's say I am logging into your system and a selecting two projects opening in two tabs. First the session is set to the project of the first opened tab, but as soon the second tab has loaded, the session will be overwritten to this project. If I then do anything in the first tab, it will be recorded for the second project.
I use:
Session state for state that should last for multiple requests, e.g. when using wizards. I'd be careful not to put too much data here though as it can lead to scalability problems.
TempData for scenarios where you only want the state to be available for the next request (e.g. when you are redirecting to another action and you want that action to have access to the state, but you don't want it to hang around after that)
Hidden form fields [input type="hidden"] for state that pertains to the form data and that I want the the controller to know about, but I don't want that data displayed. Also can be used to push state to the client so as not to overburden server resources.
ok, From what I can tell, the best option seems to be to save it into the Session data
RESTful URLs, hidden fields, and session cookies are your friends.

Resources