How Can Get Session With SessionId? - c#

I use ASP.NET MVC. I have a problem. I set my variables to the session and I request a web service that doesn't belong to me. Then the web service makes an HttpPost request to my server.
It doesn't send a cookie to my server so I lost my session.
I think I can save my sessionid to the DB and I can get back my session with this ID. But I can't get any solution.
What's your suggestion?
public ActionResult SomeAction(){
mySettingService.saveSessionIdToDb(someToken, Session.SessionID);
var myPaymentFormObj = FormInit.Create(request, options);
myPaymentFormObj.DoRequest(); //it's callback to my another action with a token
}
[HttpPost]
public ActionView MyCallBack(string someToken){
//here is our app generates new session id and i lost my session because other server doesn't send me session id.
//i need to read session id from db and i can get my session maybe.
var mySessionId = mySettingService.getSessionIdFromDb(someToken);
//how can i start session like this?
Session.SessionID = mySessionId;
}

It seems like the problem you described is about maintaining the distributed transaction.
To describe it better your app is a service A and the webServer is service B.
You can perform an action which saves some changes to the database A including the session stuff then you send a call to service B which also can saves some changes to its DB or perform a bunch of other actions but in this case you don't care how it works, you only care about what kind of responses you get back with a callback. There should be an option to be able to send some kind of unique thing like userEmail or a transactionId which you can get back in a callback method to be able to recognize the transaction.
What I would suggest you do is something like
[HttpPost]
public ActionResult SendBlah(BlahData data){
var transactionId = Guid.NetGuid();
_sessionService.Create(transactionId, dataYouWantToStore)
_webServiceB.SendBlah(transactionId, data, token);
//optionally return info about call status / do other stuff
}
//or this can be of type HttpGet
[HttpPost]
public ActionView MyCallBack(string someToken, string tranactionId){
var sessionData = _sessionService.Get(tranactionId)
//do other stuff
}
If it's needed and you are using e.g. JWT you can store the transactionId/emailAddress/etc. there instead and read it.
Btw. it's always safer to store the session in the database instead of using some cache objects or relaying on cookies or javascript objects etc.
Also, it's better to be careful with the amount of data you want to store in a Session table in your db. I'd personally focus on storing the Ids and stuff like Status of given item etc.

Related

ASP .NET Core using InMemory Cache per user

I have a system where at some point, the user will be locked to a single page. In this situation his account his locked and he cannot be redirected to any other page and this is after authentication.
The verification is done using Page Filters accessing database. To improve performance I have used memory cache.
However, the result wasn't as expected because once the cache is used for a single user it will affect all the others.
As far as i know, you can separate caching using tag helpers per user but I have no idea if this is possible using code
public async Task<IActionResult> Iniciar(int paragemId, string paragem)
{
var registoId = Convert.ToInt32(User.GetRegistoId());
if (await _paragemService.IsParagemOnGoingAsync(registoId))
{
return new JsonResult(new { started = false, message = "Já existe uma paragem a decorrer..." });
}
else
{
await _paragemService.RegistarInicioParagemAsync(paragemId, paragem, registoId);
_registoService.UpdateParagem(new ProducaoRegisto(registoId)
{
IsParado = true
});
await _registoService.SaveChangesAsync();
_cache.Set(CustomCacheEntries.RecordIsParado, true, DateTimeOffset.Now.AddHours(8));
return new JsonResult(new { started = true, message = "Paragem Iniciada." });
}
}
here i only check first if the user account is blocked in the database first without checking cache first and then create the cache entry.
Every user will be locked because of this.
So my point is... Is there a way to achieve this like tag helpers?
The CacheTagHelper is different than cache in general. It works via the request and therefore can vary on things like headers or cookie values. Just using MemoryCache or IDistributedCache directly is low-level; you're just adding values for keys directly, so there's nothing here to "vary" on.
That said, you can compose your key using something like the authenticated user's id, which would then give each user a unique entry in the cache, i.e. something like:
var cacheKey = $"myawesomecachekey-{User.FindFirstValue(ClaimTypes.NameIdentifier)}";
Short of that, you should use session storage, which is automatically unique to the user, because it's per session.
There are several alternatives to the cache. For details please see this link that describes them in greater detail.
Session State
An alternative would be to store the value in session state. This way, the session of one user does not interfere with the ones of others.
However, there are some downsides of this approach. If the session state is kept in memory, you cannot run your application in a server farm because one server does not know of the others session memory. So you would need to save the session state in a cache (REDIS?) or a database.
In addition, as session memory is stored in the server users cannot change it and avoid the redirection that you try to implement. The downside is that this reduces the amount of users that your server can handle because the server needs to have a specific amount of memory per user.
Cookies
You can send a cookie to the client and check for this cookie when the next request arrives at your server. The downside of this approach is that the user can delete the cookie. If the only consequence of a missing cookie is a request to the database, this is neglectable.
You can use session cookies that are discarded by the server when the session expires.
General
Another hint is that you need to clear the state memory when a user signs out so that with the next sign in, the state is correctly set up for the new user.

Output cache or httpcontext.current.cache for my MVC application

We are building an MVC application, where there is huge static data to be loaded when the user first time logs in.
Luckily most of the data that has to be loaded during login is all master data and doesn't change for anyusers
But since the size of the master data is huge, we felt it is best to implement caching server side as the browser might not be able to hold the data
I have read an codeproject post on OutputCache by an Microsoft MVP, he clearly explained what cache does and what are the things to keep in mind while using caching.
So i implemented all that he suggested in my controller by just adding the line
[OutputCache(Duration = 10, VaryByParam = "none",
Location=OutputCacheLocation, NoStore=true)]
above my ActionMethod.
But i could not debug whether the data is loading from cache or there is another server hit happening.
So my first question is how do i debug whether Output cache is working or not?
And then, in our previous MVC applications we used httpcontext.current.cache which worked absolutely fine.
So, here is my second question, which is why should i prefer OuputCache over httpcontext.current.cache and why not vice versa?
What difference do they offer to caching an application?
EDIT:1
This is the method in my login view controller,
public ActionResult GetRegions(string Ids)
{
objRegionsResult = GetRegionsList();
if (!string.IsNullOrEmpty(Ids))
objRegionsResult = objRegionsResult.Where(x => Ids.Split(',').Contains(x.Type.ToString())).ToList();
return Json(objRegionsResult, JsonRequestBehavior.AllowGet);
}
private List<MORegionMaster> GetRegionsList()
{
RequestUri = "Home/GetRegions";
HttpResponseMessage response = ConnectAPI(RequestUri);
if (response.IsSuccessStatusCode)
{
objRegionsResult = response.Content.ReadAsAsync<List<MORegionMaster>>().Result;
}
}
return objRegionsResult;
}
So the above method is where i hit the api controller, which inturn hits the businesslogic class and subsequently the database and returns the datatable.
We use OutputCache when we want to cache the result of an action (not static files but cache the business logic result). We use this when we want to serve the data for all users for a particular duration.
We use httpcontext.current.cache when we want to cache some data that can be used multiple times within the same request like caching "Current logged in user object" to avoid multiple db hits.
Also, lifetime of Output Cache is not limited to current http request only but the lifetime of httpcontext.current.cache is up to current http request only.

ASP.NET Web API: How to create a persistent collection across requests?

I have a Web API providing a backend to an Angular.JS web application. The backend API needs to track the state of user activities. (Example: it needs to note which content ID a user last retrieved from the API)
Most access to the API is authenticated via username/password. For these instances, it works fine for me to store the user state in our database.
However, we do need to allow "guest" access to the service. For guests, the state does need to be tracked but should not be persisted long-term (e.g. session-level tracking). I'd really like to not have to generate "pseudo users" in our user table just to store the state for guest users, which does not need to be maintained for a significant period of time.
My plan is to generate a random value and store it in the client as a cookie. (for guests only - we use bearer authentication for authenticated users.) I would then store whatever state is necessary in an in-memory object, such as a Dictionary, using the random value as a key. I could then expire items off the dictionary periodically. It is perfectly acceptable for this data to be lost if the Web API is ever relaunched, and it would even be acceptable for the dictionary to be reset say, every day at a certain time.
What I don't know how to do in WebAPI is create the dictionary object, so that it will persist across Web API calls. I basically need a singleton dictionary object that will maintain its contents for as long as the server is running the Web API (barring a scheduled clearing or programmatic flushing)
I had the idea of dumping the Dictionary off to disk every time an API call is made, and then reading it back in when it's needed, but this does not allow for multiple simultaneous in-flight requests. The only method I can think of right now is to add another database table (guest_state or something) and replicate the users table, and then setup some sort of manual method to regularly clean out the data in the guest table.
Summary: what I need is
a way to store some data persistently in a Web API backend without having to go off to a database
preferably store this data in a Dictionary object so I can use randomly-generated session IDs as the key, and an object to store the state
the data is OK to be cleared after a set period of time or on a regular basis (not too frequently, maybe a minimum of a 6 hour persistence)
I figured out a solution using the Singleton pattern:
public static class Services
{
private static Dictionary<string, string> cache;
private static object cacheLock = new object();
public static Dictionary<string,string> AppCache
{
get
{
lock (cacheLock)
{
if (cache == null)
{
cache = new Dictionary<string, string>();
}
return cache;
}
}
}
}
public class testController()
{
[HttpGet]
public HttpResponseMessage persist()
{
HttpResponseMessage hrm = Request.CreateResponse();
hrm.StatusCode = HttpStatusCode.OK;
Services.AppCache.Add(Guid.NewGuid().ToString(), DateTime.Now.ToString());
string resp = "";
foreach (string s in Services.AppCache.Keys)
{
resp += String.Format("{0}\t{1}\n", s, Services.AppCache[s]);
}
resp += String.Format("{0} records.", Services.AppCache.Keys.Count);
hrm.Content = new StringContent(resp, System.Text.Encoding.ASCII, "text/plain");
return hrm;
}
}
It seems the Services.AppCache object successfully holds onto data until either the idle timeout expires or the application pool recycles. Luckily I can control all of that in IIS, so I moved my app to its own AppPool and setup the idle timeout and recycling as appropriate, based on when I'm ok with the data being flushed.
Sadly, if you don't have control over IIS (or can't ask the admin to set the settings for you), this may not work if the default expirations are too soon for you... At that point using something like a LocalDB file or even a flat JSON file might be more useful.

How to persist keeping the info in the Session["Stuff"]?

When the user makes selection and clicks a button, I call to:
public ActionResult Storage(String data)
{
Session["Stuff"] = data;
return null;
}
Then, I redirect them to another page where the data is accessed by
#Session["Stuff"]
This far, I'm happy. What I do next is that upon a click on a button on the new page, I perform a call to:
public ActionResult Pdfy()
{
Client client = new Client();
byte[] pdf = client.GetPdf("http://localhost:1234/Controller/SecondPage");
client.Close();
return File(pdf, "application/pdf", "File.pdf");
}
Please note that the PDFization itself works perfectly well. The problem is that when I access the second page a second time (it's beeing seen by the user and looks great both in original and on reload), it turns out that Session["Stuff"] suddenly is null!
Have I started a new session by the recall?
How do I persistently retain data stored in Session["Stuff"] before?
If you're simply storing string data (as would be indicated by your method signature) in an MVC application, don't.
It's far easier to pass the data as a query parameter to each method that needs it. It's far easier to manage and doesn't rely on Session sticky-ness.
To generate the appropriate links, you can pass data to your views and use Html.ActionLink to generate your links with the appropriate parameter data.
Here's several reasons why the session variable could return null:
null is passed into Storage
Some other code sets Session["Stuff"] to null
The session times out
Something calls Session.Clear() (or Session.Abandon())
The underlying AppPool is restarted on the server
Your web server is farmed and session state is not distributed properly
The first two can be discovered by debugging.

How to pass query string parameter in asp.net?

I am using Access Control service (ACS). I fetched all identity providers (ip) which i set for my application using the following code :
public ActionResult IdentityProviders(string serviceNamespace, string appId)
{
string idpsJsonEndpoint = string.Format(Global.IdentityProviderJsonEndpoint, serviceNamespace, appId);
var client = new WebClient();
var data = client.DownloadData(idpsJsonEndpoint);
return Content(Encoding.UTF8.GetString(data), "application/json");
}
When user click over the signin link the above code called using ajax and get the ips and display them in jquery-ui dialog. And when user click any one of the ips for login the browser redirect to the selected ip login page. After successful login the control return to my control which i set as a returnUrl. Upto this every thing is works fine.
Now what i am trying to do is to pass some values to identity provider (ip) login page and want to get back those values at my returnUrl controller. For this i searched and came to know that there is a query string parameter known as wctx which we can set and get the value at return url. But i dont know how to do this. Can anybody please guid me how can i achieve this?
It is relatively (pretty) easy.
Your URL for listing IdPs looks something like this:
https://[your_namespace].accesscontrol.windows.net:443/v2/metadata/IdentityProviders.js?protocol=wsfederation&realm=[your_realm]&reply_to=[configured_return_url_for_your_rp]&context=&request_id=&version=1.0&callback=
This is the most complete request for list of Identity Providers. Your may miss some variables (such as context, or reply_to), but what I show is the complete request.
So now you have two options:
inclide your own reply_to parameter. It must be withing the configured realm. So if your realm is https://www.mygreatapp.com/, your default return URL would probably be something like https://www.mygreatapp.com/returnUrl/ (if your controller to handle ACS response is returnUrlController. Now, you can safely change the reply_to to be https://www.mygreatapp.com/returnUrl/?foo=bar, just make sure you URL Encode the query string.
Use the context parameter. It is safer to use and I would suggest using it. Now your URL for fetching list of IdPs will be something like:
https://[your_namespace].accesscontrol.windows.net:443/v2/metadata/IdentityProviders.js?protocol=wsfederation&realm=[your_realm]&reply_to=[configured_return_url_for_your_rp]&context=[your_custom_string_value_which_you_may_even_encrypt]&request_id=&version=1.0&callback=
Note the now there is context value present in the request for IdP list ([your_custom_string_value_which_you_may_even_encrypt]). In your returnUrl handler controller, you can check for it with code similar (or equal) to the following:
if (ControllerContext.HttpContext.Request.Form["wresult"] != null)
{
// This is a response from the ACS - you can further inspect the message if you will
SignInResponseMessage message =
WSFederationMessage.CreateFromNameValueCollection(
WSFederationMessage.GetBaseUrl(ControllerContext.HttpContext.Request.Url),
ControllerContext.HttpContext.Request.Form)
as SignInResponseMessage;
if (!string.IsNullOrWhiteSpace(message.Context))
{
// do whatever you want with the context value
}
}
You may want to perform any/more additional checks while handling the SignInResponse from ACS.

Categories