In ASP.NET (server side), how can I uniquely identify one browser window from another which are under the same cookiedbased sessionId - asp.net

The users of my web application may have more than one browser window (or tab) open and pointed to the same page. We're using cookie based session id's, and the user will usually work within the same session id in both browsers/tabs. I would like to be able to uniquely identify which browser window (and tab) that requested an ASP.NET page (in order to make sure, that data stored in the session does not get mixed up).
(e.g. I would be happy if the browser would generate and send a window/tab-id with the http request, as it publishes HTTP_USER_AGENT)
Any ideas?
--thomas

If I was going to implement something like this I would probably start with a Dictionary<Type, List<Guid>> and store this in the users session. I would also probably make this be a custom type that delegates the dictionary and have a factory method that works similar to
public Guid GeneratePageIdentifier(Page thepage)
{
var guid = Guid.New();
if(_dictionary[thepage.GetType()] == null)
_dictionary[thepage.GetType()] = new List<Guid> { guid };
else
((List<Guid>)_dictionary[thepage.GetType()]).Add(guid);
return guid;
}
Then embed the guid that's returned from that method on the VIewState of the page. On your page methods that execute actions that you need to validate which page it is you would be able to validate that guid is inside the collection do something. You might also want to implement a custom a type with a guid property to enscapulate more information about why you're doing this or what you need for it to be meaningful.

The Viewstate on each page will be different, maybe you can use some kind of unique identifier created on every page loaded?

It is by default not possible due to the stateless nature of the web, but you could add a "page identifier" that gets generated with each opened page and transmitted for every action.
I'd recommend that you refactor the application in a way that those mixups can't happen, no matter from which page/tab/window the request originates.

As Mark Redman said, you can use Viewstate + Session in order to store values specific to the page.
ViewState is good for storing the key (string), Session for storing whatever type of complex objects.
Use the ViewState or a hidden field in order to load at the first call a GUID.
public string PageUid
{
get
{
if (ViewState["UID"] == null)
ViewState.Add("UID", Guid.NewGuid().ToString());
return ViewState["UID"].ToString();
}
}
Then use the session to get/set your values using this key:
string MyPagesessionVariable
{
get
{
if (Session["MYVAR" + PageUid] == null)
{
Session["MYVAR" + PageUid] = "VALUE NOT SHARED WITH OTHER TABS/WINDOWS";
}
return Session["MYVAR" + PageUid];
}
set
{
Session["MYVAR" + PageUid] = value;
}
}

Related

Show a message after redirecting after a successful POST request without using TempData

I am using the Post-Redirect-Get pattern.
In my asp.net core MVC web app, this is what happens:
User submits a form via POST which adds an item to db.
Controller adds the new item and redirects with 302/303 to "/Home/Index/xxxx", where xxxx is the id of the item.
The new request (/Home/Index/xxxx) is served by the controller, and it displays the item. And the item url in the address bar is something the user can copy and share.
At step 3 above, I would like to show the user a message saying "Item was successfully added".
This is my code (without the success message):
public async Task<IActionResult> Index(string id)
{
ItemView itemView = null;
if (string.IsNullOrEmpty(id))
itemView = new ItemView(); // Create an empty item.
else
itemView = await itemService.GetItemAsync(id);
return View(itemView);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Index(ItemView itemView)
{
string id = await itemService.AddItemAsync(itemView);
return RedirectToAction("Index", "Home", new { id = id });
}
There are few ways to do this that I found in other answers on stackoverflow.
Redirect to "/Home/Index/xxxx?success=true". When action sees a success=true param, it can display the success message. But I don't want to use an extra param because I would like users to be able to just copy the url from the address bar and share it. And I don't want them sharing the url that has success param, because then everyone who clicks on the shared link will see the message "Item was successfully added".
This post suggests using TempData, which is a good solution. I think that would need me to enable sticky behavior on the server, which I would like to avoid if possible.
I can probably use referrer url to determine if the request came after a form submission, and in that case I can show the message.
The original answer by "snoopy" did point me in the right direction. But for some unknown reason, that answer no longer exists, so I am posting the answer myself in the hope it would benefit someone in future.
ASP .NET Core 1.1 and higher supports Cookie based Tempdata provider called CookieTempDataProvider. Link to Microsoft Docs.
This is similar to Session based Tempdata, but no data is stored on the server side. The response from the server set's a cookie in the browser with the data you want to store. The next request from the browser will include this cookie. The framework automatically parses this and populates this in TempData, which the controller can use. Once the controller reads this data, then the CookieTempDataProvider automatically adds the appropriate headers in the response to clear this cookie.
In your Startup class's ConfigureServices method, you need to register CookieTempDataProvider:
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
To store some data in cookie based temp data, you simple set the value like this in your controller:
TempData["key"] = "value";
To read the data in your controller, you read it like this:
string value = TempData["key"];
if (value != null)
{
// Do something with the the value.
}
The check for non-null tells you if that key exists in TempData or not. Note that you can also check using .ContainsKey() method, but that is not counted as a read. The data (& the cookie) will not be cleared unless you read it. For example this will not clear the data:
if (TempData.ContainsKey("key"))
{
// Do something without actually reading the value of TempData["key"].
}

Is it thread-safe to write this?

Can I write something like the following (in an assembly being used in an ASP.NET web page)?
public static string CurrentAuthenticatedUserFromHttpRequest
{
get
{
if (HttpContext.Current.Items["AuthUser"] == null)
{
return string.Empty;
}
return HttpContext.Current.Items["AuthUser"].ToString(); //set in "TryAuthenticate"
}
}
It is going to be a static read-only property. The value (to HttpContext.Current.Items["AuthUser"]) is set through a httphandler.
Just wondering on how this would perform during multiple requests. Is the data going to be accurate when multiple users try to access the same property (in multiple requests), even when high volumes of requests come in?
Yes, this is threadsafe. The static HttpContext.Current property differs per thread and contains the context for the request that the thread is currently handling.

how to send only one element to an other page by Post in Asp.Net

I dunno whether I described my question well but I'm trying to send some element to other page either with a hyperlink or a Response.Redirect() and it's a key which make some access to the DB so I don't want to send it via url and get it. what should I do? thanks.
You can store it in session, or you could use some type of encoding and pass it in the QueryString.
Another option would be to store the value in ViewState and use Server.Transfer to direct the user to the other page. With Server.Transfer you'll still be able to access the ViewState from the other page like this:
private string ValueFromOtherPage
{
get
{
if (ViewState["value"] != null)
{
return ViewState["value"].ToString();
}
else
{
string value;
if (Context.Handler != null)
{
value = (Context.Handler as PreviousPageName).MyStoredValue;
ViewState["value"] = value;
}
return value;
}
}
}
Take a look at this article for encoding/encrypting the QueryString:
http://www.codeproject.com/KB/web-security/QueryStringEncryptionNET.aspx
You can use session variables to store objects:
Session["VariableName"] = yourObject;
Note that overusing these session variables may lead to memory leaks.
You can save it in a session
You can send a GUID which represent some info (in the QS)
You can use cookies.

ASP.NET Object Caching in a Class

I'm trying to create a Caching Class to cache some objects from my pages. The purpose is to use the Caching system of the ASP.NET framework but to abstract it to separate class.
It seems that the caching doesn't persist.
Any ideas what I'm doing wrong here? Is it possible at all to cache object out side the Page it self?
EDIT: added the code:
Insert to cache
Cache c = new Cache();
c.Insert(userid.ToString(), DateTime.Now.AddSeconds(length), null, DateTime.Now.AddSeconds(length), Cache.NoSlidingExpiration,CacheItemPriority.High,null);
Get from the cache
DateTime expDeath = (DateTime)c.Get(userid.ToString())
I get null on the c.Get, even after I did have the key.
The code is in a different class than the page itself (the page uses it)
Thanks.
There are numerous ways you can store objects in ASP.NET
Page-level items -> Properties/Fields on the page which can live for the lifetime of the page lifecycle in the request.
ViewState -> Store items in serialised Base64 format which is persisted through requests using PostBack. Controls (including the page itself - it is a control) can preserve their previous state by loading it from ViewState. This gives the idea of ASP.NET pages as stateful.
HttpContext.Items -> A dictionary of items to store for the lifetime of the request.
Session -> Provides caching over multiple requests through session. The session cache mechanism actually supports multiple different modes.
InProc - Items are stored by the current process, which means should the process terminate/recycle, the session data is lost.
SqlServer - Items are serialised and stored in a SQL server database. Items must be serialisable.
StateServer - Items are serialised and stored in a separate process, the StateServer process. As with SqlServer, items must be serialisable.
Runtime - Items stored in the runtime cache will remain for the lifetime of the current application. Should the applciation get recycled/stop, the items will be lost.
What type of data are you trying to store, and how do you believe it must be persisted?
Right at the beginning of last year I wrote a blog post on a caching framework I had been writing, which allows me to do stuff like:
// Get the user.
public IUser GetUser(string username)
{
// Check the cache to find the appropriate user, if the user hasn't been loaded
// then call GetUserInternal to load the user and store in the cache for future requests.
return Cache<IUser>.Fetch(username, GetUserInternal);
}
// Get the actual implementation of the user.
private IUser GetUserInternal(string username)
{
return new User(username);
}
That was nearly a year ago, and it has been evolved a bit since then, you can read my blog post about it, let me know if thats of any use.
Your cache reference needs to be accessible to all items in your code - the same reference.
If you are newing up the Cache class every time, you are doing it wrong.
I have done almost the same things, but with a different code (and it work for me) :
(CacheKeys is an enum)
using System;
using System.Configuration;
using System.Web;
using System.IO;
public static void SetCacheValue<T>(CacheKeys key, T value)
{
RemoveCacheItem(key);
HttpRuntime.Cache.Insert(key.ToString(), value, null,
DateTime.UtcNow.AddYears(1),
System.Web.Caching.Cache.NoSlidingExpiration);
}
public static void SetCacheValue<T>(CacheKeys key, T value, DateTime expiration)
{
HttpRuntime.Cache.Insert(key.ToString(), value, null,
expiration,
System.Web.Caching.Cache.NoSlidingExpiration);
}
public static void SetCacheValue<T>(CacheKeys key, T value, TimeSpan slidingExpiration)
{
HttpRuntime.Cache.Insert(key.ToString(), value, null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
slidingExpiration);
}
public static T GetCacheValue<T>(CacheKeys key)
{
try
{
T value = (T)HttpRuntime.Cache.Get(key.ToString());
if (value == null)
return default(T);
else
return value;
}
catch (NullReferenceException)
{
return default(T);
}
}

Is there any page specific property to replace session["user_id"]

In my page i have used Session["user_id"] to know the status of user. Is there any page specific property to replace Session["user_id"].
Session is the best place to store user id!
You can wrap user id keeping using next extension method:
as far as Page.User returns IPrincipal then:
public static string UserId (this IPrincipal user)
{
get
{
return HttpContext.Current.State["user_id"];
}
set
{
HttpContext.Current.State["user_id"] = value;
}
}
Usage (inside a web page);
this.Request.User.UserId; // get, set
you can use a query string to store the user id instead of a session, moving it along from page to page.
another option is to use a viewstate, but this is limited per page and does not move along to other pages

Resources