Conditional output caching in ASP.NET - asp.net

I have a question on how to programmatically instruct ASP.NET to skip resolving a request from the output cache.
Imagine you've got a page output cached (e.g. http://domain/page.aspx) by means of applying cache policy settings from a CMS to the HttpResponse at runtime. On a per request basis depending on i.e. the current user is authenticated + a member of a set of known groups (or matched by business logic), I would like to instruct ASP.NET to skip resolving a request from the output cache.
The scenario is that two different users are on the system at the same time (or more). User A is authenticated + a member of a set of known groups, and user B is anonymous. Regardless of the page being output cached, I want the authenticated user to browse all pages as if no output caching was enabled - ever; at the same time, I would like ASP.NET to continue serving output cached pages to anonymous users (or, users who isn't matched by business logic).
The typical suggestion is to use VaryByHeader, VaryByParam etc. and pollute the output cache - not good, but while digging in the output cache module using Reflector, I noticed that the output cache module skips the current request in case a couple of known "cache-control" headers are present. As far as I'm concerned about headers, these are sent from the browser if the user forces a fresh copy to be rendered by hitting F5 or ENTER in the address bar.
So, what I'm doing is simply setting the "cache-control" header to "no-cache" in a custom http module in an event that goes before the ResolveRequestCache event to which the output cache subscribes. Like this:
context.Request.Headers["Cache-Control"] = "no-cache";
All is nice and dandy, however, if the HttpCachePolicy.SetValidUntilExpires(true) cache policy is set, ASP.NET disregards the request header previously set and serves the request from the output cache.
As an alternative, I guess I could write additional code in a post-processing event in the same http module to ensure that HttpCachePolicy.SetValidUntilExpires(false) is called, in case output caching has been configured, but I think it would be a more clean solution to actually be able to instruct ASP.NET to simply skip resolving the request from the output cache. I can imagine a lot of ackward solutions to this question, but I'm after the right one.
For reference, I have been trying most if not all relevant methods of the HttpCachePolicy class e.g.:
HttpResponse.Cache.SetNoServerCaching()).

You can add an HttpCacheValidateHandler to your application in which you can implement any logic you want. It will be executed during the ResolveRequestCache event which is fired after Authentication and Authorization have been executed. The key is to return HttpValidationStatus.IgnoreThisRequest in the case where you want to bypass the Cache.
See this sample HttpModule for reference:
public class CacheModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest +=
(s, e) => context.Context.Response.Cache
.AddValidationCallback(CacheHandler, null);
}
private static void CacheHandler(
HttpContext context, object data,
ref HttpValidationStatus validationstatus)
{
// bypass cache for all users with skipCache cookie
validationstatus =
context.Request.Cookies["skipCache"] != null
? HttpValidationStatus.IgnoreThisRequest
: HttpValidationStatus.Valid;
}
public void Dispose()
{
}
}

I'm not sure whether there is a supported method of doing this after something is cached.
But why don't you like the VaryByHeader option? Assuming there is a header you can look at to differentiate between logged in and not logged in users, that should work. It wouldn't pollute the cache if you have logic to only populate the output cache if the user is not logged in. Logged in users would alway get cache misses.

Related

ASP.Net Core 2.0 - ResponseCaching Middleware - Not Caching on Server

I want to use server-side response caching (output cache) with asp.net core 2.0 and found out about Response Caching Middleware and wanted to give it a try with a brand new asp.core mvc project.
Here is the description from the link above which makes me think this could be used like output cache.
The middleware determines when responses are cacheable, stores responses, and serves responses from cache.
Here is how my startup.cs looks like.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddResponseCaching();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseResponseCaching();
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
and here is the HomeController.cs
[ResponseCache(Duration = 60)]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
public IActionResult Contact()
{
ViewData["Message"] = "Your contact page.";
return View();
}
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
there is also a timestamp at the bottom of _Layout.cshtml file so i can tell when the page is rendered, like below.
<p>© 2018 - ResponseCachingMiddleware - #DateTime.UtcNow</p>
Cache-Control headers seem to be fine, this is what I get in headers when I load the page but time stamp keeps getting updated on every refresh every second.
Cache-Control:public,max-age=60
What I'm understanding from MS documentations is Response Caching Middleware is the server-side caching mechanism that takes care of caching the response while Response Caching seems to be just a filter to manipulate response headers for caching.
Can't tell if there is something wrong with my understanding or code and I wanna complain that I'm feeling this way too often since I started prototyping with ASP.Net Core. Maybe you could also suggest better resources as a side topic.
I've checked out this post before
ASP.NET Core 2.0 - Http Response Caching Middleware - Nothing cached
Also checked this out but it seems like the only difference is I'm using mvc.
https://github.com/aspnet/ResponseCaching/blob/dev/samples/ResponseCachingSample/Startup.cs
Thanks
Edit: I'm seeing the message below in the output window, cannot find anything about it on google except the few places I already checked for response caching middleware.
Microsoft.AspNetCore.ResponseCaching.ResponseCachingMiddleware:Information:
The response could not be cached for this request.
Note: I wish I could create #response-caching-middleware tag. Not sure #responsecache is relevant.
I had the same issue, I was about to pull my hairs over it, I'd set app.UseResponseCaching(); as well as services.AddResponseCaching(); and add ResponseCache on top of my action exactly like what was told in Microsoft official Docs, despite the the cache-controll header was set correctly on response returning from server but still nothing cached at server-side.
After couple of hours of sweating on this issue I figured out where the problem arises and why nothing cached at server.
Browsers by default set cache-controll value to max-age=0 for the request (if the request is not caused by back or forward) even though you set cache-controller correctly in your response by adding ResponseCache attribute on top of you action (or controller) since the cache-controller sent by request is set to max-age=0, the server is unable to cache response, I think this must be added to list of Response Caching limitation as well
Anyway you can override browser default behavior by adding few line of code right before calling app.UseResponseCaching(); on the other hand you need to add a custom middle-ware to modify request cache-control header value before calling app.UseResponseCaching();.
See code below, worked for me hope work for you too
app.Use(async (ctx, next) =>
{
ctx.Request.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue()
{
Public = true,
MaxAge = TimeSpan.FromSeconds(60)
};
await next();
}
);
app.UseResponseCaching();
for ensuring that ResponseCaching works as expected you can also use postman but you must set 'Send no-cache Header' to off in the setting, see image below
I had this same confusion recently.
ASP.Net Core's ResponseCaching does provide both client-side caching (through HTTP response headers) & server-side (through a memory cache'd middleware that short-circuits other middlewares if the response is in the cache). The server-side portion reads the HTTP response cache headers to determine if it should do server-side caching (similar to what an ISP or CDN might do).
Unfortunately, debugging the server-side ResponseCaching is tricky because it has weird rules & there's not adequate logging. In my case I pulled down Microsoft's source code to step through it & find the issue with my code.
The note you found in the output window "The response could not be cached for this request" is a clue.
There's 2 parts to the server-side caching of a request. The server has to prime the cache the first time the url is requested. It will serve the cached version the 2nd time it's requested. Pay attention to when the error message shows up, if it's on the 1st or 2nd request. That'll tell you if it couldn't be stored in the cache or if it couldn't be retrieved from the cache.
The rules for both storage & retrieval are in this source code file:
https://github.com/aspnet/ResponseCaching/blob/3bf5f6a1ce69b65c998d6f5c739822a9bed4a67e/src/Microsoft.AspNetCore.ResponseCaching/Internal/ResponseCachingPolicyProvider.cs
Your "Cache-Control:public,max-age=60" header should match these rules just fine.
My guess is you actually had it working, but didn't know how to test it correctly.
There is a counter-intuitive portion of ResponseCaching noted in this issue: https://github.com/aspnet/Home/issues/2607
Essentially, if the browser sends a no-cache or no-store header (when you hit CTRL+F5 or have your debugger tools open), ASP.Net Core's ResponseCaching will honor the browser's request & re-generate the response.
So, to test if your code was working you probably loaded the page, which primed the cache, then you hit CTRL+F5 to force-refresh your browser & you expected the server-side to respond with a cached entry rather than running your WebAPI code. However, it honored the no-cache request header & bypassed the cache (& wrote that message in your output log).
The way to test this would be to clear your browser cache in-between requests (or switch to incognito), rather than using CTRL+F5.
On a side note, honoring the no-cache/no-store request headers was probably a poor design choice since ASP.Net Core's ResponseCache will most likely be used by a server who owns the response, rather than an intermediary cache like a CDN/ISP. I've extended the base ResponseCache with an option to disable honoring these headers (as well as serialize the cache to disk, rather than in-memory only). It's an easy drop-in replacement for the default cache.
You can find my extension here:
https://github.com/speige/AspNetCore.ResponseCaching.Extensions
https://www.nuget.org/packages/AspNetCore.ResponseCaching.Extensions
There are also a few other other gotchas with ResponseCaching to watch out for which you may have already read about in the blog urls you posted. Authenticated requests & responses with set-cookie won't be cached. Only requests using GET or HEAD method will be cached. If the QueryString is different, it'll make a new cache entry. Also, usually you'll want a "Vary" header to prevent caching if certain conditions of a request differ from the previously-cached request (example: user-agent, accept-encoding, etc). Finally, if a Middleware handles a request it'll short-circuit later Middlewares. Make sure your app.UseResponseCaching() is registered before app.UseMVC()
If the Cache-Control header is coming through, then it's working. That's all the server can do from that perspective. The client ultimately makes the decision whether or not to actually cache the resource. Sending the header doesn't force the client to do anything; in fact, the server, in general, cannot force the client to do anything.

Use of HttpResponse.DisableKernelCache

When working in ASP.NET, HttpResponse objects have a DisableKernelCache() method. E.g., a HttpHandler could:
public void ProcessRequest(HttpContext context)
{
context.Response.DisableKernelCache();
...
MSDN helpfully describes this method as:
Disables kernel caching for the current response.
Why would I want to use this function?
By "kernel caching," they are referring to caching done by the HTTP driver, http.sys.
With kernel caching enabled (which happens when you enable OutputCaching with default parameters and don't use a query string in your URLs), the content is returned to the user without any callbacks into user mode. You might want to disable that in cases where you need to serve different content to different users or if you need to prematurely expire the cache, etc.
Some ASP.NET features, such as VaryByContentEncoding, implicitly disable kernel caching for you, in order to function correctly.

SessionID changing across different instances in Azure (and probably in a web farm)

I have a problem with an Azure project with one WebRole but multiple instances that uses cookieless sessions. The application doesn't need Session storage, so it's not using any session storage provider, but I need to track the SessionID. Apparently, the SessionID should be the same accross the WebRole instances, but it changes suddently w/o explanation. We are using the SessionID to track some data, so it's very important.
In order to reproduce the issue:
Create a Cloud Project.
Add a ASP.NET Web Role. The code already in it will do.
Open Default.aspx
Add a control to see the current SessionID and a button to cause a postback
<p><%= Session.SessionID %></p>
<asp:Button ID="Button1" runat="server" Text="PostBack" onclick="Button1_Click" />
Add a event handler for button that will delay the response a bit:
protected void Button1_Click(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(150);
}
Open Web.Config
Enable cookieless sessions:
<system.web>
<sessionState cookieless="true" />
</system.web>
Run the project, and hit fast and repeteadly the "PostBack" button for a while giving attention to the session id in the address bar. Nothing happens, the session id is always the same :). Stop it.
Open ServiceConfiguration.csfg
Enable four instances:
<Instances count="4" />
Ensure that in the Web.config there is a line related with the machine key that has been added automatically by Visual Studio. (at the end of system.web).
Rerun the project, hit fast and repeteadly the "Postback" button for a while and give attention to the session id in the address bar. You'll see how the SessionID changes after a while.
Why is this happening? As far as I know, if all machines share the machineKey, the session should be the same across them. With cookies there are no problems, the issue apparently is just when cookieless sessions are used.
My best guess, is that something wrong is happening when there are several instances, when the SessionID generated in one WebRole goes to another, is rejected and regenerated. That doesn't make sense, as all the WebRoles have the same machineKey.
In order to find out the problem, and see it more clearly, I created my own SessionIDManager:
public class MySessionIDManager : SessionIDManager
{
public override string CreateSessionID(HttpContext context)
{
if (context.Items.Contains("AspCookielessSession"))
{
String formerSessionID = context.Items["AspCookielessSession"].ToString();
// if (!String.IsNullOrWhiteSpace(formerSessionID) && formerSessionID != base.CreateSessionID(context))
// Debugger.Break();
return formerSessionID;
}
else
{
return base.CreateSessionID(context);
}
}
}
And to use it change this line in the WebConfig:
<sessionState cookieless="true" sessionIDManagerType="WebRole1.MySessionIDManager" />
Now you can see that the SessionID doesn't change, no matter how fast and for how long you hit. If you uncomment those two lines, you will see how ASP.NET is creating a new sessionID even when there is already one.
In order to force ASP.NET to create a new session, just a redirect to an absolute URL in your site:
Response.Redirect(Request.Url.AbsoluteUri.Replace(Request.Url.AbsolutePath, String.Empty));
Why is this thing happening with cookieless sessions?
How reliable is my solution in MySessionIDManager ?
Kind regards.
UPDATE:
I've tried this workaround:
User-Specified Machine Keys
Overwritten by Site-Level Auto
Configuration, but the problem
still stands.
public override bool OnStart()
{
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
using (var server = new ServerManager())
{
try
{
// get the site's web configuration
var siteNameFromServiceModel = "Web"; // update this site name for your site.
var siteName =
string.Format("{0}_{1}", RoleEnvironment.CurrentRoleInstance.Id, siteNameFromServiceModel);
var siteConfig = server.Sites[siteName].GetWebConfiguration();
// get the appSettings section
var appSettings = siteConfig.GetSection("appSettings").GetCollection()
.ToDictionary(e => (string)e["key"], e => (string)e["value"]);
// reconfigure the machine key
var machineKeySection = siteConfig.GetSection("system.web/machineKey");
machineKeySection.SetAttributeValue("validationKey", appSettings["validationKey"]);
machineKeySection.SetAttributeValue("validation", appSettings["validation"]);
machineKeySection.SetAttributeValue("decryptionKey", appSettings["decryptionKey"]);
machineKeySection.SetAttributeValue("decryption", appSettings["decryption"]);
server.CommitChanges();
_init = true;
}
catch
{
}
}
return base.OnStart();
}
I've also tried this about put a
session start handler and add
some data, but no luck.
void Session_Start(object sender, EventArgs e)
{
Session.Add("dummyObject", "dummy");
}
Bounty up!
In short, unless you use cookies or a session provider there is no way for the session id to pass from one web role instance to the other. The post you mention says that the SessionID does NOT stay the same across web roles if you don't use cookies or session storage.
Check this previous question for ways to handle state storage in Azure, e.g. using Table Storage
The machineKey has nothing to do with sessions or the application domain, it is the key used to encrypt,decrypt,validate authentication and viewstate data. To verify this open SessionIDManager.CreateSessionID with Reflector. You will see that the ID value is just a random 16-byte value encoded as a string.
The AspCookielessSession value is already checked by SessionIDManager in the GetSessionID method, not CreateSessionID so the check is already finished before your code gets executed. Since the default sessionstate mode is InProc it makes sence that separate web roles will not be able to validate the session key so they create a new one.
In fact, a role may migrate to a different physical machine at any time, in which case its state will be lost. This post from the SQL Azure Team describes a way to use SQL Azure to store state for exactly this reason.
EDIT I finally got TableStorageSessionStateProvider to work in cookieless mode!
While TableStorageSessionStateProvider does support cookieless mode by overriding SessionStateStoreProviderBase.CreateUnititializedItem, it fails to handle empty sessions properly in private SessionStateStoreData GetSession(HttpContext context, string id, out bool locked, out TimeSpan lockAge,out object lockId, out SessionStateActions actions,bool exclusive). The solution is to return an empty SessionStateStoreData if no data is found in the underlying blob storage.
The method is 145 lines long so I won't paste it here. Search for the following code block
if (actions == SessionStateActions.InitializeItem)
{
// Return an empty SessionStateStoreData
result = new SessionStateStoreData(new SessionStateItemCollection(),
}
This block returns an empty session data object when a new session is created. Unfortunately the empty data object is not stored to the blob storage.
Replace the first line with the following line to make it return an empty object if the blob is empty:
if (actions == SessionStateActions.InitializeItem || stream.Length==0)
Long stroy short cookieles session state works as long as the provider supports it. You'll have to decide whether using cookieless state justifies using a sample provider though. Perhaps vtortola should check the AppFabric Caching CTP. It includes out-of-the-box ASP.NET providers, is a lot faster and it definitely has better support than the sample providers. There is even a step-by-step tutorial on how to set session state up with it.
Sounds tricky.
I have one suggestion/question for you. Don't know if it will help - but you sound like you're ready to try anything!
It sounds like maybe the session manager on the new machine is checking the central session storage provider and, when it finds that the session storage is empty, then it's issuing a new session key.
I think a solution may come from:
- using Session_Start as you have above in order to insert something into Session storage
- plus inserting a persistent Session storage provider of some description into the web.config - e.g. some of the oldest Azure samples provide a table based provider, or some of the newer samples provide an AppFabric caching solution.
I know your design is not using the session storage, but maybe you need to put something in (a bit like your Session_Start), plus you need to define something other than in-process session management.
Alternatively, you need to redesign your app around something other than ASP.NET sessions.
Hope that helps - good luck!
I experienced the same problem and after much research and debugging I found that the issue occurred because the "virtual servers" in the Azure SDK map the websites to different paths in the IIS metabase. (You can see this through through Request.ServerVariables["APPL_MD_PATH"].)
I just found this out now but wanted to post this so people could get working on testing it. My theory is that this problem may go away once it's published out to Azure proper. I'll update with any results I find.

Access Forms Authentication from Separate HTTP Handler

I'm just reading about implementing my own HTTP handler for ASP.NET 4.0 and IIS7. This looks really cool. I want special processing for ZIP files and it seems like an HTTP handler is the perfect solution.
However, what's giving me trouble is that the handler must be in a separate assembly. So how can I access the rest of my application from this assembly?
Specifically, I'd like to determine if the user is authenticated and redirect them to the login page if they are not. But User.Identity.IsAuthenticated, etc. will not be available from my handler.
(Yes, I know there are ways to approach this without an HTTP handler but they don't seem appropriate for my specific needs.)
User.Identity.IsAuthenticated, etc. will not be available from my handler.
The ProcessRequest method gives you the current HTTP context from which you could determine if the user is authenticated:
public void ProcessRequest(HttpContext context)
{
if (!context.User.Identity.IsAuthenticated)
{
// the user is not authenticated
}
...
}

ASP.NET: Session.SessionID changes between requests

Why does the property SessionID on the Session-object in an ASP.NET-page change between requests?
I have a page like this:
...
<div>
SessionID: <%= SessionID %>
</div>
...
And the output keeps changing every time I hit F5, independent of browser.
This is the reason
When using cookie-based session state, ASP.NET does not allocate storage for session data until the Session object is used. As a result, a new session ID is generated for each page request until the session object is accessed. If your application requires a static session ID for the entire session, you can either implement the Session_Start method in the application's Global.asax file and store data in the Session object to fix the session ID, or you can use code in another part of your application to explicitly store data in the Session object.
http://msdn.microsoft.com/en-us/library/system.web.sessionstate.httpsessionstate.sessionid.aspx
So basically, unless you access your session object on the backend, a new sessionId will be generated with each request
EDIT
This code must be added on the file Global.asax. It adds an entry to the Session object so you fix the session until it expires.
protected void Session_Start(Object sender, EventArgs e)
{
Session["init"] = 0;
}
There is another, more insidious reason, why this may occur even when the Session object has been initialized as demonstrated by Cladudio.
In the Web.config, if there is an <httpCookies> entry that is set to requireSSL="true" but you are not actually using HTTPS: for a specific request, then the session cookie is not sent (or maybe not returned, I'm not sure which) which means that you end up with a brand new session for each request.
I found this one the hard way, spending several hours going back and forth between several commits in my source control, until I found what specific change had broken my application.
In my case I figured out that the session cookie had a domain that included www. prefix, while I was requesting page with no www..
Adding www. to the URL immediately fixed the problem. Later I changed cookie's domain to be set to .mysite.com instead of www.mysite.com.
my problem was that we had this set in web.config
<httpCookies httpOnlyCookies="true" requireSSL="true" />
this means that when debugging in non-SSL (the default), the auth cookie would not get sent back to the server. this would mean that the server would send a new auth cookie (with a new session) for every request back to the client.
the fix is to either set requiressl to false in web.config and true in web.release.config or turn on SSL while debugging:
Using Neville's answer (deleting requireSSL = true, in web.config) and slightly modifying Joel Etherton's code, here is the code that should handle a site that runs in both SSL mode and non SSL mode, depending on the user and the page (I am jumping back into code and haven't tested it on SSL yet, but expect it should work - will be too busy later to get back to this, so here it is:
if (HttpContext.Current.Response.Cookies.Count > 0)
{
foreach (string s in HttpContext.Current.Response.Cookies.AllKeys)
{
if (s == FormsAuthentication.FormsCookieName || s.ToLower() == "asp.net_sessionid")
{
HttpContext.Current.Response.Cookies[s].Secure = HttpContext.Current.Request.IsSecureConnection;
}
}
}
Another possibility that causes the SessionID to change between requests, even when Session_OnStart is defined and/or a Session has been initialized, is that the URL hostname contains an invalid character (such as an underscore). I believe this is IE specific (not verified), but if your URL is, say, http://server_name/app, then IE will block all cookies and your session information will not be accessible between requests.
In fact, each request will spin up a separate session on the server, so if your page contains multiple images, script tags, etc., then each of those GET requests will result in a different session on the server.
Further information: http://support.microsoft.com/kb/316112
My issue was with a Microsoft MediaRoom IPTV application. It turns out that MPF MRML applications don't support cookies; changing to use cookieless sessions in the web.config solved my issue
<sessionState cookieless="true" />
Here's a REALLY old article about it:
Cookieless ASP.NET
in my case it was because I was modifying session after redirecting from a gateway in an external application, so because I was using IP instead on localhost in that page url it was actually considered different website with different sessions.
In summary
pay more attention if you are debugging a hosted application on IIS instead of IIS express and mixing your machine http://Ip and http://localhost in various pages
In my case this was happening a lot in my development and test environments. After trying all of the above solutions without any success I found that I was able to fix this problem by deleting all session cookies. The web developer extension makes this very easy to do. I mostly use Firefox for testing and development, but this also happened while testing in Chrome. The fix also worked in Chrome.
I haven't had to do this yet in the production environment and have not received any reports of people not being able to log in. This also only seemed to happen after making the session cookies to be secure. It never happened in the past when they were not secure.
Update: this only started happening after we changed the session cookie to make it secure. I've determined that the exact issue was caused by there being two or more session cookies in the browser with the same path and domain. The one that was always the problem was the one that had an empty or null value. After deleting that particular cookie the issue was resolved. I've also added code in Global.asax.cs Sessin_Start method to check for this empty cookie and if so set it's expiration date to something in the past.
HttpCookieCollection cookies = Response.Cookies;
for (int i = 0; i < cookies.Count; i++)
{
HttpCookie cookie = cookies.Get(i);
if (cookie != null)
{
if ((cookie.Name == "ASP.NET_SessionId" || cookie.Name == "ASP.NET_SessionID") && String.IsNullOrEmpty(cookie.Value))
{
//Try resetting the expiration date of the session cookie to something in the past and/or deleting it.
//Reset the expiration time of the cookie to one hour, one minute and one second in the past
if (Response.Cookies[cookie.Name] != null)
Response.Cookies[cookie.Name].Expires = DateTime.Today.Subtract(new TimeSpan(1, 1, 1));
}
}
}
This was changing for me beginning with .NET 4.7.2 and it was due to the SameSite property on the session cookie. See here for more info: https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/
The default value changed to "Lax" and started breaking things. I changed it to "None" and things worked as expected.
Be sure that you do not have a session timeout that is very short, and also make sure that if you are using cookie based sessions that you are accepting the session.
The FireFox webDeveloperToolbar is helpful at times like this as you can see the cookies set for your application.
Session ID resetting may have many causes. However any mentioned above doesn't relate to my problem. So I'll describe it for future reference.
In my case a new session created on each request resulted in infinite redirect loop. The redirect action takes place in OnActionExecuting event.
Also I've been clearing all http headers (also in OnActionExecuting event using Response.ClearHeaders method) in order to prevent caching sites on client side. But that method clears all headers including informations about user's session, and consequently all data in Temp storage (which I was using later in program). So even setting new session in Session_Start event didn't help.
To resolve my problem I ensured not to remove the headers when a redirection occurs.
Hope it helps someone.
I ran into this issue a different way. The controllers that had this attribute [SessionState(SessionStateBehavior.ReadOnly)] were reading from a different session even though I had set a value in the original session upon app startup. I was adding the session value via the _layout.cshtml (maybe not the best idea?)
It was clearly the ReadOnly causing the issue because when I removed the attribute, the original session (and SessionId) would stay in tact. Using Claudio's/Microsoft's solution fixed it.
I'm on .NET Core 2.1 and I'm well aware that the question isn't about Core. Yet the internet is lacking and Google brought me here so hoping to save someone a few hours.
Startup.cs
services.AddCors(o => o.AddPolicy("AllowAll", builder =>
{
builder
.WithOrigins("http://localhost:3000") // important
.AllowCredentials() // important
.AllowAnyMethod()
.AllowAnyHeader(); // obviously just for testing
}));
client.js
const resp = await fetch("https://localhost:5001/api/user", {
method: 'POST',
credentials: 'include', // important
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
Controllers/LoginController.cs
namespace WebServer.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class UserController : ControllerBase
{
[HttpPost]
public IEnumerable<string> Post([FromBody]LoginForm lf)
{
string prevUsername = HttpContext.Session.GetString("username");
Console.WriteLine("Previous username: " + prevUsername);
HttpContext.Session.SetString("username", lf.username);
return new string[] { lf.username, lf.password };
}
}
}
Notice that the session writing and reading works, yet no cookies seem to be passed to the browser. At least I couldn't find a "Set-Cookie" header anywhere.

Resources