How can I clear browser cache only on logout, sure I can use the below:
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
Response.Cache.SetNoStore();
But this particular page which is a shopping bag page is accessible by both login and non-login users. How can I set it in such a way whereby the login user is able to access this page without clearing the browser cache but Only clears it when he/she logs out so that another user will not be able to access the history contents.
I have tried the solutions here:
http://www.codeproject.com/Tips/135121/Browser-back-button-issue-after-logout
made some changes but still couldn't figure out how to deal with this issue.
I also cleared my session on logout as below but I understand the browser cache will still stay.
FormsAuthentication.SignOut();
Session.Abandon();
Response.Redirect("~/");
Please advice. Thanks.
I am not a c# expert, but I am pretty sure what you have above only tells the browser to not cache the page you are on. There is no way to tell the browser to clear cache on any page. This would be a problem if there was such a way. Sounds like the solution you need is to not cache any page at all, regardless of logging out or not.
Perhaps you are getting muddled with the difference between server and client cache?
If you set output cache on your aspx page, that's server-side cache, and you have a scenario where .NET can decide whether to send pre-cached content or not, and still apply ACL rules.
If you set cache requirements on the HTTP you return using Response.Cache, that's client-side caching. Once the browser obeys the cache rules you send here, the only opportunity you will have to retract your cache rules is the next time the browser requests the page. If you set the cache to expire tomorrow, that's the next chance you'll get to amend the caching. Assuming the browser is obeying you, by the way, of which there is no guarantee.
In short, dynamic pages should not attempt to set client-side caching if you want them to stay dynamic. In fact you should actively use techniques such as the ones you mentioned to suppress Caching on those pages at all times.
Client-side caching should really only be used to assist with performance and bandwidth on the static parts of your site.
I am trying to solve a similar problem myself. This is just speculation, but if i could track a user specific header in my requests I was going to try using
HttpContext.Current.Response.Cache.VaryByHeaders["login"] = true;
in the global.asax
public override string GetVaryByCustomString(HttpContext context, string arg)
{
if (arg == "login")
{
return User().Name;
}
return base.GetVaryByCustomString(context, arg);
}
There is a way to do it. If you are caching a page, you can add a vary parameter. For Example
[OutputCache(Duration = 60, Location = System.Web.UI.OutputCacheLocation.Client, VaryByParam = "random")]
[CompressFilter]
public ActionResult Page(PageModel model)
{
...
}
In the example above, if I pass a random variable like the ticks of the current datetime object, that will prevent the cache.
Related
The [Authorize] is wonderful for locking pages down but I am building a new product with few users and it makes no sense that it directs people to Login, because there is no one to login yet. It should direct them to Register instead.
But I am struggling to find an easy way to do that without a ton of middleware.
You can change the LoginPath on start up, but I suspect this does not answer your question because when enough users exists then what happens?
To change the login path you can add:
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(cookieOptions =>
{
cookieOptions.LoginPath = "/register";
cookieOptions.AccessDeniedPath = "/account/denied";
cookieOptions.ExpireTimeSpan = TimeSpan.FromMinutes(120);
});
However if you are wanting a switch when you have reached a critical mass then this will not suffice.
EDIT one approach could be something like:
Create a loginOrRegister page. Then on this page hit the database (or whatever you use to see whether you have hit critical mass or its a known user based on a cookie) and then either
Redirect to Login
OR
Rediect to register
I am facing an issue in MVC that i am able to visit the previous page on browser back button click even after getting logged out. I have few approaches:
1) Disable the Browser back button using window.history.forward().
This will give bad user experience.
2) Using outputCacheAttribute by providing the duration=0 but this will restrict both server side and client side caching. SO don't want to use this.
3) Adding below method in global.asax.cs
protected void Application_BeginRequest()
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetExpires(DateTime.UtcNow.AddHours(-1));
Response.Cache.SetNoStore();
}
Third approach will not allow to make the copy of the cache to the browser. Also to make this work I have to add the [Authorize] attribute on each controller.
This is not the best option for me as I have hundreds of controller. And tomorrow if I will add new controller then again I have to decorate the Authorize attribute to that new controller.
Is there any other approach that any one of you can suggest.
You can only add this attribute:
[OutputCache(NoStore = true, Duration = 0, Location = OutputCacheLocation.None)]
Public ActionResult SomeAction()
{
//
}
Disabling cache in specific action should be enough I guess.
Or if you still do not want to destroy cache just couple of places,
you can do that in your LOGIN function, you can add previous Attribute, or just use this when someone signs out:
Session.Clear();
Session.Abandon();
I wish it will help, since I havent got much time on testing it myself.
In an MVC App that I'm working on, we had to block browser cache on all actions for security reasons (preventing user from going back in history after he has logged out). We achieved this using this solution.
However, we do want to allow browsers to cache css and js bundles. Unfortunately the solution mentioned above appears to block caching of all resources. On local machine it even includes static files like images, though on remote server IIS handles those files (rather than the App itself) so that's one less thing to worry about. Anyway, is there some way to tweak this solution to allow bundles to be cached by browser?
I know that I could use a filter like this one and add it to all the actions (or even better, all controllers) or add a new base controller, that has this filter by default, and set all my controllers to inherit from it, but are there any better alternatives (ones that don't involve changing myriad files in the project)?
P.S. Having written this question has made me think of a few solutions that I have to try. This happened to me before. I mean, finding the right answer while writing a question here, but I ended up not posting those questions.
The solution which appeared to me while writing this question is really simple. Just write a simple if condition inside of Application_BeginRequest to determine if the resource should be cacheable or not based on the request url... I haven't tested it yet, but it sounds like it might just do the job.
We accomplished your original requirement using a global filter. In Global.asax.cs:
GlobalFilters.Filters.Add(new NoCacheAttribute());
NoCacheAttribute:
/// <summary>
/// An attribute that can be attached to an individual controller or used globally to prevent the browser from caching the response.
/// This has nothing to do with server side caching, it simply alters the response headers with instructions for the browser.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class NoCacheAttribute : ActionFilterAttribute
{
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
if (!filterContext.IsChildAction && !(filterContext.Result is FileResult))
{
filterContext.HttpContext.Response.Cache.SetExpires(DateTime.MinValue);
filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
filterContext.HttpContext.Response.Cache.SetNoStore();
base.OnResultExecuting(filterContext);
}
}
}
This affects all of our controller actions, but leaves static content and everything else alone. The Bundling framework handles its own caching: it basically tells browsers to cache forever, but includes a cache-busting token in the URL, which is a hash that changes if any of the bundled files are modified. This mechanism is unaffected by this filter. (I don't know if that's because global filters are not applied, or because it produces a FileResult--I suspect the former.)
Here is the solution that I mentioned in the original question. It's very simple (and somewhat dirty), but it appears to work.
protected void Application_BeginRequest()
{
// Here we check if the request was for bundle or not and depending on that
// apply the cache block.
if (!Request.Url.PathAndQuery.StartsWith("/bundles/"))
{
Response.Cache.SetAllowResponseInBrowserHistory(false);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
Response.Cache.SetExpires(DateTime.Now);
Response.Cache.SetValidUntilExpires(true);
}
}
In my local envoriment I also added /Content/ folder to the condition, but it's redundant on the remote server, since IIS will handle those (unless you explicitly tell it not to).
Is it possible to vary the output cache in MVC based on certain values in the session? I've read a lot about using the varybycustom functionality and overriding GetVaryByCustomString in Global.asax but the session is not available at this point.
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom == "somekey")
//Want to check the session here (but it isn't available).
return base.GetVaryByCustomString(context, custom);
}
I understand this is because the Session isn't created until later in the request pipeline.
My concern is that without varying the cache based on the user's session, the page (which changes based on what the user has in the session, has additional HTML specific to that user etc) will get cached (as the URL is the same) and served by our load balancer, proxy servers etc. and then served to other requests with other people's session information on the page!
The reason the URL is the same is that the user comes in as a 'guest', enters some information (POST), this is validated and stored in the session and then they are re-directed back to the same page (which should now be specific to the user based on the session data).
The page itself should be cached normally because if a 'guest' visits the same URL, it should serve the same 'standard' page every time.
Is is possible to vary the caching in this way?
If you want to personalize the cache output per user, it is better you set the Location to OutputCacheLocation.Client as below. More information here
[OutputCache(Duration=3600, VaryByParam="none", Location=OutputCacheLocation.Client, NoStore=true)]
public string GetName()
{
return "Hi " + User.Identity.Name;
}
Would a Output Cache ActionFilter help at all?
Or perhaps you could refactor your view in to a layout page plus partial views for anonymous and authenticated sections, then utilize Partial Caching.
You should look into "Donut Caching", but this isn`t supported by ASP.NET MVC 3, at least not out of the box. Fortunately somebody already solved this problem for you see MvcDonutCaching
I read that ASP.NET MVC 4 will include "Donut Hole Caching" out of the box, but i cant tell if it's in the current RC or not.
I have two pages, NonMember.aspx and Member.aspx. If a user comes to the site, they will go to NonMember.aspx as soon as they login, I want them to immediately be redirected to Member.aspx, but instead of doing this, it is staying on NonMember.aspx. The user actually has to go click on the menu item again to get to Member.aspx.
The links are located at http://abc.com/tools/NonMember.aspx
and http://abc.com/tools/Member.aspx.
I was doing:
System.IO.Path.GetFileNameWithoutExtension(Request.Url.ToString());
but I am thinking there is a better way, especially since I have multiple default.aspx pages and this could pose a problem
Here is more detail on what exactly I am doing:
When I run the site on my local development machine, the NonMember page points to:
http://testserver/tools/NonMember.aspx.
Requet.Url.AbsolutePath points to /testserver/tools/NonMember.aspx.
Then I am doing this:
if(url == "~/tools/NonMember.aspx")
{
Response.Redirect("~/tools/Member.aspx");
}
The above is not working and I can check if url is equal to /testserver/tools/NonMember.aspx because if I deploy to liveserver, it will fail.
When using Forms Authentication for an ASP.NET application, it will automatically redirect you to the page you were viewing before you logged in. This is why you are redirected back to the NonMember.aspx page.
It would be better if you had just one member page, and perform a check in the page to see if the user is authenticated, if so, display the member content, otherwise, display the non-member content.
Then, when the user logs in and is redirected back to the page, they will see the member content.
If you are insistent on keeping the two separate pages, then in your check you simply have to see if the current user is authenticated (through the IsAuthenticated property on the User that is exposed through the page) and then redirect to your members page. If you are on the NonMember page, you don't need to check to see what the url is (unless this is MVC, which you didn't indicate).
If you have the HttpResponse object, you can use HttpResponse.Redirect
You should check your cookie or session variable to see if the user is logged in and then use a Response.Redirect to move them to the member page.
I'm not sure I get your scenario but, let's try.
If you have public pages, the login page and private pages (member pages) and once you authenticate your users you want them to browse ONLY the private part of your website, you should check early in the processing steps (AuthorizeRequest would be a good phase) if the request comming in is for a public or for the login page which you also consider public and redirect to your private page from there (like...having a "home" page for the member area of the site and always redirecting to it once you get an authenticated and properly authorized request to some public URL of your site).
EDIT: You should check for Request.FilePath
I ended up just doing the following:
if(Authenticated)
{
string path = Request.UrlReferrer.AbsolutePath;
if (path.EndsWith("/tools/NonMember.aspx"))
{
Response.Redirect("~/tools/Member.aspx");
}
}