Overridden GetVaryByCustomString method being called only on first request to a page - asp.net

I have the following question regarding outputcache work in either ASP.Net or Composite C1 CMS (don't know where's the problem):
I need to vary cache by cookie named "City". In Global.asax I've overridden the GetVaryByCustomString method.
public override string GetVaryByCustomString(HttpContext context, string custom)
{
var result = ApplicationLevelEventHandlers.GetVaryByCustomString(context, custom);
if (result == null) {
return base.GetVaryByCustomString(context, custom);
}
HttpCookie cookie = context.Request.Cookies["City"];
string additionalVary = "";
if (cookie != null)
additionalVary = cookie.Value;
else
additionalVary = "Москва";
return String.Concat(result, additionalVary);
}
And the problem is: the breakpoint of my overridden GetVaryByCustomString method is being hit only on the first request to a page, but isn't being hit on next requests until the cache actually expires (60 seconds in my case).
While the cookie might have been changed on the client side during that 60 seconds - the page is simply taken from the cache.
Where am I wrong?
Thanks in advance.
UPDATE:
I was finally able to make outputcache vary by cookie by the following modifications in the CacheProfile settings:
<outputCacheProfiles>
<add name="C1Page" duration="60" varyByCustom="C1Page" varyByParam="*" varyByHeader="Cookie" location="Server" />
</outputCacheProfiles>
In this case the method GetVaryByCustomString is being hit on each request, but there's no need in it: these settings make the cache vary by every cookie might be set on the website. Of course it is not a desired behavior - that's why I'm continuing the investigation.
What is even more funny - these 2 parameters (varyByHeader="Cookie" and location="Server") work only together, disabling one of them make the things look exactly as before - outputcache not varying.

Try to put a break point to Application_BeginRequest, it could be that browser isn't making a request but rather showing a cached page.

Related

Client side caching using Last-Modified header and OutputCacheAttribute in asp.net mvc 3

Edited
I want to cache images on the client and know that there are different ways to do it in mvc 3: (correct me if I'm wrong)
1) You can use OutputCacheAttribute which works with the help of Expires http header. But it will return 304 Not Modified unless the time expire (even if the image was changed).
2) To avoid displaing stale images You can use Last-Modified http header (with OutputCacheAttribute). In this case the browser sends the request to the server with If-Modified-Since http header. On the server You verify whether the object is still valid or not and if it is You just return Last-Modified http header (and the browser takes image from the local cache); if the object was modified You return it with 200 OK status.
So, the browser needs to send the request to the server each time before taking image from it's own cache. Here is the example -
3) There is another way (as I was told the right way in my case, cause the images will change very rarely... anyway, I need to implement exactly this): To add modified date to the image url and set caching with Expires for the eternity (1 year or more). If the image have changed You should send new url with new version.
Here is the code:
public class LastModifiedCacheAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Result is FilePathResult)
{
var result = (FilePathResult)filterContext.Result;
var lastModify = File.GetLastWriteTime(result.FileName);
if (!HasModification(filterContext.RequestContext, lastModify))
filterContext.Result = NotModified(filterContext.RequestContext, lastModify);
SetLastModifiedDate(filterContext.RequestContext, lastModify);
}
base.OnActionExecuted(filterContext);
}
private static void SetLastModifiedDate(RequestContext requestContext, DateTime modificationDate)
{
requestContext.HttpContext.Response.Cache.SetLastModified(modificationDate);
}
private static bool HasModification(RequestContext context, DateTime modificationDate)
{
var headerValue = context.HttpContext.Request.Headers["If-Modified-Since"];
if (headerValue == null)
return true;
var modifiedSince = DateTime.Parse(headerValue).ToLocalTime();
return modifiedSince < modificationDate;
}
private static ActionResult NotModified(RequestContext response, DateTime lastModificationDate)
{
response.HttpContext.Response.Cache.SetLastModified(lastModificationDate);
return new HttpStatusCodeResult(304, "Page has not been modified");
}
}
And I registered the LastModifiedCacheAttribute in Global.asax and applied the following OutputCacheAttribute to my action method.
[HttpGet, OutputCache(Duration = 3600, Location = OutputCacheLocation.Client, VaryByParam = "productId")]
public FilePathResult GetImage(int productId)
{ // some code }
If I use the code above seems like the browser doesn't send requests to the server, instead it just take images from the cache unless the duration is not ended. (When I change the image the browser doesn't display new version)
Questions:
1) How to implement the third approach, so that the browser will take the images from client cache (and will not send the response to the server each time it wants the image) unless the image was modified?
edited: the actual code will be appreciated.
2) In the code above the time of the first image request is written to the Last-Modified (don't know why). How to write the modification date of the file into Last-Modified?
edited: this question relates to second approach. Also, if I cache only on the client and use Last-Modified implementation I get 304 Not Modified status only if I press F5. If I reenter the same url I will get 200 OK. If I cache on a client without using Last-Modified it will always return 200 OK no matter what. How could this be explained?
You could look into using ETags (http://en.wikipedia.org/wiki/HTTP_ETag), that's the first thing I thought of reading your question.
You could also have a look here: Set ETag for FileResult - MVC 3
If I understood correctly, what you are after, is having infinite caching, and rely on invalidating the cache by changing the actual url of the resource.
In that case, I believe the actual implementation is much simpler and doesn't require manual handling of headers.
Ultimately, the point is to be able to have the images loaded by a URL such as the following:
http://someDomain/product/image/1?v={something}
With "1" being the productId, and specifying some sort of version identifier ("v").
The key is to build that url, where the value of v is dependent on the last modification of the image (which you should probably store along with the image, or the product). You can probably hash the modification date, and use that.
If the next time you build the url, the date of the last modification is the same, you will get the same hash value, and therefore render the same url as before, and the browser will load that from cache without requesting anything from the server.
As soon as the image is updated, and that modification date changes, your code will generate a different url, which will force the browser to request it again.
Then you just apply the OutputCache attribute to the Action, configured to cache on the client (no need to specify VaryByParam) and you should be set.
You can use the VaryByCustom option with Output Caching to achieve this without using a custom attribute. Change your method code like this:
[HttpGet, OutputCache(Duration = 3600,
Location = OutputCacheLocation.Client,
VaryByCustom = "imagedate")]
public FilePathResult GetImage(int productId)
{ // some code }
Then add the following code to your Global.asax:
public override string GetVaryByCustomString(System.Web.HttpContext context, string custom)
{
if (custom.ToLower() == "imagedate")
{
return System.IO.File.GetLastWriteTime(Server.MapPath("~/Images/my-image.png")).ToString();
}
else
{
return base.GetVaryByCustomString(context, custom);
}
}
When the timestamp of the image file changes, the return value of the GetVaryByCustomString method will change which will cause ASP.NET to reload the image rather than use the cached value.
See http://msdn.microsoft.com/en-us/library/aa478965.aspx for further details.
Intially used this answer but for some reason when image is modified it doesn't update in client, instead shows the cached version of image.
So the solution is using versioning which is your 3'rd option, just add LastUpdated datetime field from your product image database
Action Method
[HttpGet]
[OutputCache(
Duration = 7200,
VaryByParam = "productId;lastUpdated",
Location = OutputCacheLocation.Client)]
public ActionResult GetImage(string productId, string lastUpdated)
{
var dir = Server.MapPath("~/productimages/");
var path = Path.Combine(dir, productId + ".jpg");
return base.File(path, "image/jpeg");
}
In View
<img src="#Url.Action("GetImage", "Home", new { productId = "test-product-100",
lastUpdated =Model.LastUpdated })" />
Idea taken from this post.
Answer is late but Hope helps someone.

Last-Modified Header in MVC

I have recently come across the Last-Modified Header.
How and where can I include it in MVC?
What are the advantages of including it?
I want an example how last modified header can be included in an mvc project, for static pages and database queries as well?
Is it different from outputcache, if yes how?
Basically, I want the browser to clear the cache and display the latest data or pages automatically, without the need for the user to do a refresh or clearing the cache.
The Last-Modified is mainly used for caching. It's sent back for resources for which you can track the modification time. The resources doesn't have to be files but anything. for instance pages which are generated from dB information where you have a UpdatedAt column.
Its used in combination with the If-Modified-Since header which each browser sends in the Request (if it has received a Last-Modified header previously).
How and where can I include it in MVC?
Response.AddHeader
What are the advantages of including it?
Enable fine-grained caching for pages which are dynamically generated (for instance you can use your DB field UpdatedAt as the last modified header).
Example
To make everything work you have to do something like this:
public class YourController : Controller
{
public ActionResult MyPage(string id)
{
var entity = _db.Get(id);
var headerValue = Request.Headers["If-Modified-Since"];
if (headerValue != null)
{
var modifiedSince = DateTime.Parse(headerValue).ToLocalTime();
if (modifiedSince >= entity.UpdatedAt)
{
return new HttpStatusCodeResult(304, "Page has not been modified");
}
}
// page has been changed.
// generate a view ...
// .. and set last modified in the date format specified in the HTTP rfc.
Response.AddHeader("Last-Modified", entity.UpdatedAt.ToUniversalTime().ToString("R"));
}
}
You might have to specify a format in the DateTime.Parse.
References:
HTTP status codes
HTTP headers
Disclamer: I do not know if ASP.NET/MVC3 supports that you manage Last-Modified by yourself.
Update
You could create an extension method:
public static class CacheExtensions
{
public static bool IsModified(this Controller controller, DateTime updatedAt)
{
var headerValue = controller.Request.Headers['If-Modified-Since'];
if (headerValue != null)
{
var modifiedSince = DateTime.Parse(headerValue).ToLocalTime();
if (modifiedSince >= updatedAt)
{
return false;
}
}
return true;
}
public static ActionResult NotModified(this Controller controller)
{
return new HttpStatusCodeResult(304, "Page has not been modified");
}
}
And then use them like this:
public class YourController : Controller
{
public ActionResult MyPage(string id)
{
var entity = _db.Get(id);
if (!this.IsModified(entity.UpdatedAt))
return this.NotModified();
// page has been changed.
// generate a view ...
// .. and set last modified in the date format specified in the HTTP rfc.
Response.AddHeader("Last-Modified", entity.UpdatedAt.ToUniversalTime().ToString("R"));
}
}
UPDATE: Check my new answer
How and where can I include it in MVC?
The built-in OutputCache filter does the job for you and it uses those headers for caching.
The OuputCache filter uses the Last-Modified header when you set the Location as Client or ServerAndClient.
[OutputCache(Duration = 60, Location = "Client")]
public ViewResult PleaseCacheMe()
{
return View();
}
What are the advantages of including it?
Leveraging client-side caching with conditional cache flush
I want an example how last modified header can be included in an mvc project, for static pages and database queries as well?
This link contains enough information to try out a sample. For static pages like html, images IIS will take care of setting/checking the Last-Modified header and it uses the file's last modified date. For database queries you can go for setting the SqlDependency in the OutputCache.
Is it different for outputcache, if yes how? When do I need to include Last-Modified Header and when to use outputcache?
OutputCache is an action filter used for implementing caching mechanism in ASP.NET MVC. There are different ways you could perform caching using OutputCache: client-side caching, server-side caching. Last-Modified header is one way to accomplish caching in the client-side. OutputCache filter uses it when you set the Location as Client.
If you go for client-side caching (Last-Modified or ETag) the browser cache will automatically get updated in subsequent request and you don't need to do F5.
Last-Modified vs. OutputCache
The OutputCache attribute controls output caching on your IIS WebServer. This is a vendor specific server feature (see Configure IIS 7 Output Caching).
I also suggest to read Cache Exploration in ASP.NET MVC3 if you are interested in the powerful capabilities of this technology.
Last-Modified response header and it's counterpart If-Modified-Since request header are representatives of the validation cache concept (section cache control).
These headers are part of the HTTP protocol and specified in rfc4229
OutputCache and validation are not exclusive, you can combine it.
What caching scenario makes me happy?
As usual: it depends.
Configuring a 5 second OutputCache on a 100 hits/second page would drastically reduce the load. Using OutputCache, 499 out of 500 hits can be served from cache (and do not cost db roundtrip, calculations, rendering).
When I have to serve rarely changes immediately, then the validation scenario could save a lot of bandwith. Specially when you serve large content compared to a lean 304 status message. However, changes are adopted immediately since every request validates for changes in the source.
Last-Modified attribute implementation sample
Based on my experience I would recommend to implement the validation scenario (last modified) as an action filter attribute.
(Btw: Here's an other caching scenario implemented as an attribute)
Static content from file
[LastModifiedCache]
public ActionResult Static()
{
return File("c:\data\static.html", "text/html");
}
Dynamic content sample
[LastModifiedCache]
public ActionResult Dynamic(int dynamicId)
{
// get data from your backend (db, cache ...)
var model = new DynamicModel{
Id = dynamivId,
LastModifiedDate = DateTime.Today
};
return View(model);
}
public interface ILastModifiedDate
{
DateTime LastModifiedDate { get; }
}
public class DynamicModel : ILastModifiedDate
{
public DateTime LastModifiedDate { get; set; }
}
The LastModifiedCache attribute
public class LastModifiedCacheAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Result is FilePathResult)
{
// static content is served from file in my example
// the last file write time is taken as modification date
var result = (FilePathResult) filterContext.Result;
DateTime lastModify = new FileInfo(result.FileName).LastWriteTime;
if (!HasModification(filterContext.RequestContext, lastModify))
filterContext.Result = NotModified(filterContext.RequestContext, lastModify);
SetLastModifiedDate(filterContext.RequestContext, lastModify);
}
if (filterContext.Controller.ViewData.Model is HomeController.ILastModifiedDate)
{
// dynamic content assumes the ILastModifiedDate interface to be implemented in the model
var modifyInterface = (HomeController.ILastModifiedDate)filterContext.Controller.ViewData.Model;
DateTime lastModify = modifyInterface.LastModifiedDate;
if (!HasModification(filterContext.RequestContext, lastModify))
filterContext.Result = NotModified(filterContext.RequestContext, lastModify);
filterContext.RequestContext.HttpContext.Response.Cache.SetLastModified(lastModify);
}
base.OnActionExecuted(filterContext);
}
private static void SetLastModifiedDate(RequestContext requestContext, DateTime modificationDate)
{
requestContext.HttpContext.Response.Cache.SetLastModified(modificationDate);
}
private static bool HasModification(RequestContext context, DateTime modificationDate)
{
var headerValue = context.HttpContext.Request.Headers["If-Modified-Since"];
if (headerValue == null)
return true;
var modifiedSince = DateTime.Parse(headerValue).ToLocalTime();
return modifiedSince < modificationDate;
}
private static ActionResult NotModified(RequestContext response, DateTime lastModificationDate)
{
response.HttpContext.Response.Cache.SetLastModified(lastModificationDate);
return new HttpStatusCodeResult(304, "Page has not been modified");
}
}
How to enable global LastModified suppport
You can add the LastModifiedCache attribute to the RegisterGlobalFilters section of your global.asax.cs to globally enable this type of caching in your mvc project.
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
...
filters.Add(new LastModifiedCacheAttribute());
...
}
Note that outputcache isn't your only option here, and in fact you may well not want to handle last-modified in the way it does. To clarify a few options:
Option 1 - use [OutputCache]
In this case the framework will cache the response body according to whatever duration you specify. It will serve it with Last-Modified set to the current time, and max-age set to the time remaining until the original cache duration expires. If a client sends a request with If-Modified-Since then the framework will correctly return a 304. Once the cached response expires then the Last-Modified date will be updated each time a new response is cached.
Pros: caching happens at the controller level (so can work for partial content or the same cached content on different end URLs). You have better control over cacheability - e.g. HttpCacheability.ServerAndPrivate allows your server to cache the content, but not intermediate proxies.
Cons: you have no control over last-modified. When your cache expires all clients will need to re-download the content even if it hasn't actually changed
Option 2 - specify settings on Response.Cache
asp.net has another layer of caching outside of the outputcacheattribute in the form of System.Web.OutputCacheModule which all requests pass through. This behaves like an HTTP cache immediately in front of your application. So if you set sensible cache headers without applying the OutputCacheAttribute then your response will be cached here instead. For example:
Response.Cache.SetLastModified(lastModifiedDate);
Response.Cache.SetCacheability(HttpCacheability.Public);
Response.Cache.SetExpires(DateTime.Now + timespan);
Based on the above then the outputcachemodule would cache your content and any requests to the same URL would be served from the cache. Requests with If-Modified-Since would get 304s. (You could use ETags equally). When your cache expires the next request will hit your app normally, but if you know the content hasn't changed you can return the same Last-Modified or ETag as you did previously. Once this next response has been cached then subsequent clients will then be able to extend their cache lifetimes without re-downloading the content
Pros: If you have a meaningful way of determining last-modified or ETag then you have full control over it and can reduce the number of duplicate downloads.
Cons: Caching is only at the request/URL level. Only works if you're happy to set cache-control: public
Although this option reduces the likelihood of unnecessary content downloads, it doesn't eliminate it - the first request after the (server) cache expires will be served normally and result in a 200, even if a 304 would have been appropriate. That's probably for the best though, since it enables the cache to grab a fresh copy of the response body which it would have discarded when it expired previously, and hence future requests can be served straight from cache. I believe that HTTP caches can in theory be cleverer than this and use 304s to extend their own cache lifetime, but the asp.net one appears not to support that.
(Edited to replace SetMaxAge with SetExpires in the code above - seems IIS/asp.net won't respect max-age headers unless you also SetSlidingExpiration(true) but that setting appears to prevent the caching we want)
This is my second answer after doing some quite research on caching and OutputCache.
Let me answer your second question first.
What are the advantages of including it?
Browser caches the responses returned from the server. The caching is controlled by mainly three headers: Cache-Control, Last-Modified and Expires (there are others like ETag also comes to play).
The Last-Modified header tells the browser when does the resource has been modified at last. The resource could be either static file or dynamically created view. Whenever the browser makes the request for that resource it checks with the server "Hey, I already have a response for this request and it's Last-Modified date is so and so.. see the user is already tired... if you return a 304 I'm glad to use the response from my cache else please send your new response quick". (Note that the browser passes the Last-Modified value returned previously by the server in a new header called If-Modified-Since)
Ideally the server should read the value from the If-Modified-Since header and has to check with the current modified date and if they are same then it should return 304 (NOT MODIFIED) or it should return the new copy of the resource again passing the current modified date in the Last-Modified header.
The advantage is browser caching. By leveraging the browser caching the server can avoid creating a duplicate response and also it can return a new response if the cached response in the browser looks like old. The ultimate goal is save the time.
How and where can I include it in MVC?
In the case of static resources like images, html files and others you don't need to worry about setting How and Where because IIS takes care of that job. IIS uses the file's last modified date as the Last-Modified header value.
In the case of dynamic pages like a html content returned through an MVC action, how you can determine the Last-Modified header value? The dynamic driven pages are mostly data driven and it's our responsibility to decide whether the response returned previously is stale or not.
Let's say you have a blog and you have a page whether you display the details of an article (not any other details) then the page's version is decided by the last modified date or created date (if the article is not modified yet) of the article. So you have to do the same work answered by #jgauffin in the corresponding action that delivers the view.
You have asked in the comment Should I include it per action in the controller?
If you could able to abstract away the logic of reading the last modified date from database from the actions then you could accomplish the job through an action filter avoiding duplicating the code throughout actions. The question is how you are going to abstract the details away from the actions? Like passing the table/column names to the attribute? You have to figure it out!
As an example..
[LastModifiedCacheFilter(Table = "tblArticles", Column = "last_modified")]
public ViewResult Post(int postId)
{
var post = ... get the post from database using the postId
return View(post);
}
The pseudo code (means I haven't tested this :) of the LastModifiedCacheFilterAttribute implementation shown below uses Table/Column to read the last modified date but it could be some other ways as well. The idea is in the OnActionExecuting method we are doing the check and returning a 304 (if the cache is still fresh) and in the OnResultExecuted method we are reading/setting the latest modified date.
public class LastModifiedCacheFilterAttribute : ActionFilterAttribute
{
// Could be some other things instead of Table/Column
public string Table { get; set; }
public string Column { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// var lastModified = read the value from the passed Column/Table and set it here
var ifModifiedSinceHeader = filterContext.RequestContext.HttpContext.Request.Headers["If-Modified-Since"];
if (!String.IsNullOrEmpty(ifModifiedSinceHeader))
{
var modifiedSince = DateTime.Parse(ifModifiedSinceHeader).ToLocalTime();
if (modifiedSince >= lastModified)
{
filterContext.Result = new EmptyResult();
filterContext.RequestContext.HttpContext.Response.Cache.SetLastModified(lastModified.ToUniversalTime());
filterContext.RequestContext.HttpContext.Response.StatusCode = 304;
}
}
base.OnActionExecuting(filterContext);
}
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
// var lastModified = read the value from the passed Column/Table and set it herefilterContext.RequestContext.HttpContext.Response.Cache.SetLastModified(lastModified.ToUniversalTime());
base.OnResultExecuted(filterContext);
}
}
Why can't OutputCache attribute?
As per my analysis, OutputCache attribute not uses Last-Modified caching mechanism. The other thing is it uses the old page caching mechanism making it difficult to customize/extend.
Do you really need to implement the last-modified mechanism in all your actions?
Really not required. You could implement the last-modified mechanism to the actions that takes more time to create such a response and it takes more time to travel the response down the wire and reach the browser. In other cases I feel it just an overhead implementing throughout all the actions and also you have to measure out the benefits before doing so. The other main point is, in many cases the version of the page is not just decided by a single table column it could be by many other things and in those cases it may be more complicated to implement this!
A point about ETag
Though the question is about Last-Modified header I should tell something about ETag before clicking the Post Your Answer button. Compared to Last-Modified (which relies on datetime) header ETag header (relies on a hash value) is more accurate in determining whether the cached response in the browser is fresh or not but it could be little complicated to implement. IIS also includes ETag header along with the Last-Modified header for static resources. Before implementing any of this mechanism google out and see whether there is any library out there that helps you out!

sharing session data between domains in asp.net, confused!

Ok, here's my problem, i want to maintain session data between two applications or domains (eg:- www.abc.com and secure.abc.com).
I have read on net about this, but many people pointing many different ways to do it, with people commenting +ve and -ve responses to all. Plus many are just providing theoretical answer, do this and that ,but no code at all.
are these steps all that is required?
1) in web.config: <httpCookies domain=".abc.com"/>
2) store session data in sql DB as:(after preparing the db for storing sessions)
<sessionState mode="SQLServer" sqlConnectionString="Data Source=YourServer;
Integrated Security=True;database=MySessionDB" sqlCommandTimeout="30"
allowCustomSqlDatabase="true"/>
<machineKey decryption="AES" validation="SHA1" decryptionKey="..." validationKey="..." />
3)Am confused about this one: i want to set the domain for the session cookie like this
Response.Cookies["ASP.NET_SessionId"].Domain = ".abc.com";
But where should this code be written?
this entry: http://mgrzyb.blogspot.com/2007/12/aspnet-and-subdomains.html says: use System.Web.SessionState.SessionIDManager as a base class but the SaveSessionID method is not virtual so cannot be overridden. Options are: either explicitly re-implement the interface method or decorate SessionIDManager class and after calling SessionIDManager.SaveSessionID set Response.Cookies[SessionIdCookieName].Domain to our domain.
Only if the author had provided real code, step 3 would have been clear.
Can anyone provide the code for it.
Plus all this 3 steps enough to share session among the domains?
the 3rd step statement can be written in global.asax according to: http://www.know24.net/blog/ASPNET+Session+State+Cookies+And+Subdomains.aspx
protected void Application_PreRequestHandlerExecute(Object sender, EventArgs e)
{
/// only apply session cookie persistence to requests requiring session information
#region session cookie
if (Context.Handler is IRequiresSessionState || Context.Handler is IReadOnlySessionState )
{
/// Ensure ASP.NET Session Cookies are accessible throughout the subdomains.
if (Request.Cookies["ASP.NET_SessionId"] != null && Session != null && Session.SessionID != null)
{
Response.Cookies["ASP.NET_SessionId"].Value = Session.SessionID;
Response.Cookies["ASP.NET_SessionId"].Domain = ".abc.com"; // the full stop prefix denotes all sub domains
Response.Cookies["ASP.NET_SessionId"].Path = "/"; //default session cookie path root
}
}
#endregion
}

Refresh ASP.NET Role Provider

simple question...
Given I have an ASP.NET site, which uses a [custom] RoleProvider,
Is there any way in which I can somehow "refresh" the provider without forcing the user to log out of the site and log back in?
I'm looking for something that would be akin to a fictional method
Roles.Refresh()
Specifically, I am looking at this for if an administrator changes a user's roles, the user sessions could maybe refresh themselves every 10 minutes or something.
I assume you have something like this in your web.config:
<roleManager enabled="true" defaultProvider="..."
cacheRolesInCookie="true">
The roles are cached in a cookie , so you can force them to refresh by deleting the cookie. This method worked for me. I added the cookieName attribute so that I don't rely on asp.net's default. For your scenario, though, you may be able to just set the cookieTimeout attribute to something reasonable and be done with it.
This method won't update the roles immediately, of course. They will be updated on the next page load after you delete the cookie.
Refresh just need to delete the cookie:
For C#: Roles.DeleteCookie(); // Works as Roles.Refresh()
If you don't want to use cookies you can use Session object to cache the roles.
like this:
public override string[] GetRolesForUser(string username)
{
System.Web.SessionState.HttpSessionState Session = HttpContext.Current.Session;
if (Session["roles"] == null)
Session["roles"] = MyDataProvider.Security.GetRolesForUser(username);
return (string[])Session["roles"];
}
When you need to update the roles for this user you can do
Session["roles"] = null
depend on the custom role provider used.
Just call a "update my role" function on every request? (bad way but at least your sure to update it)
The roles are cached in a cookie (encrypted of course). The simplest solution will be to disable caching in the web.config file. You will loose some performance.
Else you must somehow resend the auth cookie. One major problem is that many browsers will not accept cookies on redirects with method post.
The other solution that worked for me:
1) In a aspx methodod log the user out and store the username in the session
//Add User to role reviewer and refresh ticket
Roles.AddUserToRole(User.Identity.Name, Constants.ROLE_REVISOR);
FormsAuthentication.SignOut();
FormsAuthentication.SetAuthCookie(User.Identity.Name, false); //Might work in some browsers
Session["REFRESHROLES"] = User.Identity.Name;
Response.Redirect("someprotectedurl?someid=" + someid);
2) In the loginpage sign the user in again if username is stored in session
protected void Page_Load(object sender, EventArgs e)
{
string returnUrl = Request.QueryString["ReturnUrl"];
if(String.IsNullOrEmpty(returnUrl) == false)
{
if(Session["REFRESHROLES"] != null)
{
if(!string.IsNullOrEmpty(Session["REFRESHROLES"].ToString()))
{
FormsAuthentication.SetAuthCookie(Session["REFRESHROLES"].ToString(), false);
Session.Remove("REFRESHROLES");
Response.Redirect(returnUrl);
return;
}
}

A potentially dangerous Request.Form value was detected from the client

Every time a user posts something containing < or > in a page in my web application, I get this exception thrown.
I don't want to go into the discussion about the smartness of throwing an exception or crashing an entire web application because somebody entered a character in a text box, but I am looking for an elegant way to handle this.
Trapping the exception and showing
An error has occurred please go back and re-type your entire form again, but this time please do not use <
doesn't seem professional enough to me.
Disabling post validation (validateRequest="false") will definitely avoid this error, but it will leave the page vulnerable to a number of attacks.
Ideally: When a post back occurs containing HTML restricted characters, that posted value in the Form collection will be automatically HTML encoded.
So the .Text property of my text-box will be something & lt; html & gt;
Is there a way I can do this from a handler?
I think you are attacking it from the wrong angle by trying to encode all posted data.
Note that a "<" could also come from other outside sources, like a database field, a configuration, a file, a feed and so on.
Furthermore, "<" is not inherently dangerous. It's only dangerous in a specific context: when writing strings that haven't been encoded to HTML output (because of XSS).
In other contexts different sub-strings are dangerous, for example, if you write a user-provided URL into a link, the sub-string "javascript:" may be dangerous. The single quote character on the other hand is dangerous when interpolating strings in SQL queries, but perfectly safe if it is a part of a name submitted from a form or read from a database field.
The bottom line is: you can't filter random input for dangerous characters, because any character may be dangerous under the right circumstances. You should encode at the point where some specific characters may become dangerous because they cross into a different sub-language where they have special meaning. When you write a string to HTML, you should encode characters that have special meaning in HTML, using Server.HtmlEncode. If you pass a string to a dynamic SQL statement, you should encode different characters (or better, let the framework do it for you by using prepared statements or the like)..
When you are sure you HTML-encode everywhere you pass strings to HTML, then set ValidateRequest="false" in the <%# Page ... %> directive in your .aspx file(s).
In .NET 4 you may need to do a little more. Sometimes it's necessary to also add <httpRuntime requestValidationMode="2.0" /> to web.config (reference).
There's a different solution to this error if you're using ASP.NET MVC:
ASP.NET MVC – pages validateRequest=false doesn’t work?
Why is ValidateInput(False) not working?
ASP.NET MVC RC1, VALIDATEINPUT, A POTENTIAL DANGEROUS REQUEST AND THE PITFALL
C# sample:
[HttpPost, ValidateInput(false)]
public ActionResult Edit(FormCollection collection)
{
// ...
}
Visual Basic sample:
<AcceptVerbs(HttpVerbs.Post), ValidateInput(False)> _
Function Edit(ByVal collection As FormCollection) As ActionResult
...
End Function
In ASP.NET MVC (starting in version 3), you can add the AllowHtml attribute to a property on your model.
It allows a request to include HTML markup during model binding by skipping request validation for the property.
[AllowHtml]
public string Description { get; set; }
If you are on .NET 4.0 make sure you add this in your web.config file inside the <system.web> tags:
<httpRuntime requestValidationMode="2.0" />
In .NET 2.0, request validation only applied to aspx requests. In .NET 4.0 this was expanded to include all requests. You can revert to only performing XSS validation when processing .aspx by specifying:
requestValidationMode="2.0"
You can disable request validate entirely by specifying:
validateRequest="false"
For ASP.NET 4.0, you can allow markup as input for specific pages instead of the whole site by putting it all in a <location> element. This will make sure all your other pages are safe. You do NOT need to put ValidateRequest="false" in your .aspx page.
<configuration>
...
<location path="MyFolder/.aspx">
<system.web>
<pages validateRequest="false" />
<httpRuntime requestValidationMode="2.0" />
</system.web>
</location>
...
</configuration>
It is safer to control this inside your web.config, because you can see at a site level which pages allow markup as input.
You still need to programmatically validate input on pages where request validation is disabled.
The previous answers are great, but nobody said how to exclude a single field from being validated for HTML/JavaScript injections. I don't know about previous versions, but in MVC3 Beta you can do this:
[HttpPost, ValidateInput(true, Exclude = "YourFieldName")]
public virtual ActionResult Edit(int id, FormCollection collection)
{
...
}
This still validates all the fields except for the excluded one. The nice thing about this is that your validation attributes still validate the field, but you just don't get the "A potentially dangerous Request.Form value was detected from the client" exceptions.
I've used this for validating a regular expression. I've made my own ValidationAttribute to see if the regular expression is valid or not. As regular expressions can contain something that looks like a script I applied the above code - the regular expression is still being checked if it's valid or not, but not if it contains scripts or HTML.
In ASP.NET MVC you need to set requestValidationMode="2.0" and validateRequest="false" in web.config, and apply a ValidateInput attribute to your controller action:
<httpRuntime requestValidationMode="2.0"/>
<configuration>
<system.web>
<pages validateRequest="false" />
</system.web>
</configuration>
and
[Post, ValidateInput(false)]
public ActionResult Edit(string message) {
...
}
You can HTML encode text box content, but unfortunately that won't stop the exception from happening. In my experience there is no way around, and you have to disable page validation. By doing that you're saying: "I'll be careful, I promise."
The answer to this question is simple:
var varname = Request.Unvalidated["parameter_name"];
This would disable validation for the particular request.
You can catch that error in Global.asax. I still want to validate, but show an appropriate message. On the blog listed below, a sample like this was available.
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
if (ex is HttpRequestValidationException)
{
Response.Clear();
Response.StatusCode = 200;
Response.Write(#"[html]");
Response.End();
}
}
Redirecting to another page also seems like a reasonable response to the exception.
http://www.romsteady.net/blog/2007/06/how-to-catch-httprequestvalidationexcep.html
For MVC, ignore input validation by adding
[ValidateInput(false)]
above each Action in the Controller.
Please bear in mind that some .NET controls will automatically HTML encode the output. For instance, setting the .Text property on a TextBox control will automatically encode it. That specifically means converting < into <, > into > and & into &. So be wary of doing this...
myTextBox.Text = Server.HtmlEncode(myStringFromDatabase); // Pseudo code
However, the .Text property for HyperLink, Literal and Label won't HTML encode things, so wrapping Server.HtmlEncode(); around anything being set on these properties is a must if you want to prevent <script> window.location = "http://www.google.com"; </script> from being output into your page and subsequently executed.
Do a little experimenting to see what gets encoded and what doesn't.
In the web.config file, within the tags, insert the httpRuntime element with the attribute requestValidationMode="2.0". Also add the validateRequest="false" attribute in the pages element.
Example:
<configuration>
<system.web>
<httpRuntime requestValidationMode="2.0" />
</system.web>
<pages validateRequest="false">
</pages>
</configuration>
If you don't want to disable ValidateRequest you need to implement a JavaScript function in order to avoid the exception. It is not the best option, but it works.
function AlphanumericValidation(evt)
{
var charCode = (evt.charCode) ? evt.charCode : ((evt.keyCode) ? evt.keyCode :
((evt.which) ? evt.which : 0));
// User type Enter key
if (charCode == 13)
{
// Do something, set controls focus or do anything
return false;
}
// User can not type non alphanumeric characters
if ( (charCode < 48) ||
(charCode > 122) ||
((charCode > 57) && (charCode < 65)) ||
((charCode > 90) && (charCode < 97))
)
{
// Show a message or do something
return false;
}
}
Then in code behind, on the PageLoad event, add the attribute to your control with the next code:
Me.TextBox1.Attributes.Add("OnKeyPress", "return AlphanumericValidation(event);")
Another solution is:
protected void Application_Start()
{
...
RequestValidator.Current = new MyRequestValidator();
}
public class MyRequestValidator: RequestValidator
{
protected override bool IsValidRequestString(HttpContext context, string value, RequestValidationSource requestValidationSource, string collectionKey, out int validationFailureIndex)
{
bool result = base.IsValidRequestString(context, value, requestValidationSource, collectionKey, out validationFailureIndex);
if (!result)
{
// Write your validation here
if (requestValidationSource == RequestValidationSource.Form ||
requestValidationSource == RequestValidationSource.QueryString)
return true; // Suppress error message
}
return result;
}
}
It seems no one has mentioned the below yet, but it fixes the issue for me. And before anyone says yeah it's Visual Basic... yuck.
<%# Page Language="vb" AutoEventWireup="false" CodeBehind="Example.aspx.vb" Inherits="Example.Example" **ValidateRequest="false"** %>
I don't know if there are any downsides, but for me this worked amazing.
I guess you could do it in a module; but that leaves open some questions; what if you want to save the input to a database? Suddenly because you're saving encoded data to the database you end up trusting input from it which is probably a bad idea. Ideally you store raw unencoded data in the database and the encode every time.
Disabling the protection on a per page level and then encoding each time is a better option.
Rather than using Server.HtmlEncode you should look at the newer, more complete Anti-XSS library from the Microsoft ACE team.
If you're using framework 4.0 then the entry in the web.config (<pages validateRequest="false" />)
<configuration>
<system.web>
<pages validateRequest="false" />
</system.web>
</configuration>
If you're using framework 4.5 then the entry in the web.config (requestValidationMode="2.0")
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" requestValidationMode="2.0"/>
</system.web>
If you want for only single page then, In you aspx file you should put the first line as this :
<%# Page EnableEventValidation="false" %>
if you already have something like <%# Page so just add the rest => EnableEventValidation="false" %>
I recommend not to do it.
In ASP.NET, you can catch the exception and do something about it, such as displaying a friendly message or redirect to another page... Also there is a possibility that you can handle the validation by yourself...
Display friendly message:
protected override void OnError(EventArgs e)
{
base.OnError(e);
var ex = Server.GetLastError().GetBaseException();
if (ex is System.Web.HttpRequestValidationException)
{
Response.Clear();
Response.Write("Invalid characters."); // Response.Write(HttpUtility.HtmlEncode(ex.Message));
Response.StatusCode = 200;
Response.End();
}
}
I found a solution that uses JavaScript to encode the data, which is decoded in .NET (and doesn't require jQuery).
Make the textbox an HTML element (like textarea) instead of an ASP one.
Add a hidden field.
Add the following JavaScript function to your header.
function boo() {
targetText = document.getElementById("HiddenField1");
sourceText = document.getElementById("userbox");
targetText.value = escape(sourceText.innerText);
}
In your textarea, include an onchange that calls boo():
<textarea id="userbox" onchange="boo();"></textarea>
Finally, in .NET, use
string val = Server.UrlDecode(HiddenField1.Value);
I am aware that this is one-way - if you need two-way you'll have to get creative, but this provides a solution if you cannot edit the web.config
Here's an example I (MC9000) came up with and use via jQuery:
$(document).ready(function () {
$("#txtHTML").change(function () {
var currentText = $("#txtHTML").text();
currentText = escape(currentText); // Escapes the HTML including quotations, etc
$("#hidHTML").val(currentText); // Set the hidden field
});
// Intercept the postback
$("#btnMyPostbackButton").click(function () {
$("#txtHTML").val(""); // Clear the textarea before POSTing
// If you don't clear it, it will give you
// the error due to the HTML in the textarea.
return true; // Post back
});
});
And the markup:
<asp:HiddenField ID="hidHTML" runat="server" />
<textarea id="txtHTML"></textarea>
<asp:Button ID="btnMyPostbackButton" runat="server" Text="Post Form" />
This works great. If a hacker tries to post via bypassing JavaScript, they they will just see the error. You can save all this data encoded in a database as well, then unescape it (on the server side), and parse & check for attacks before displaying elsewhere.
Cause
ASP.NET by default validates all input controls for potentially unsafe contents that can lead to cross-site scripting (XSS) and SQL injections. Thus it disallows such content by throwing the above exception. By default it is recommended to allow this check to happen on each postback.
Solution
On many occasions you need to submit HTML content to your page through Rich TextBoxes or Rich Text Editors. In that case you can avoid this exception by setting the ValidateRequest tag in the #Page directive to false.
<%# Page Language="C#" AutoEventWireup="true" ValidateRequest = "false" %>
This will disable the validation of requests for the page you have set the ValidateRequest flag to false. If you want to disable this, check throughout your web application; you’ll need to set it to false in your web.config <system.web> section
<pages validateRequest ="false" />
For .NET 4.0 or higher frameworks you will need to also add the following line in the <system.web> section to make the above work.
<httpRuntime requestValidationMode = "2.0" />
That’s it. I hope this helps you in getting rid of the above issue.
Reference by: ASP.Net Error: A potentially dangerous Request.Form value was detected from the client
The other solutions here are nice, however it's a bit of a royal pain in the rear to have to apply [AllowHtml] to every single Model property, especially if you have over 100 models on a decent sized site.
If like me, you want to turn this (IMHO pretty pointless) feature off site wide you can override the Execute() method in your base controller (if you don't already have a base controller I suggest you make one, they can be pretty useful for applying common functionality).
protected override void Execute(RequestContext requestContext)
{
// Disable requestion validation (security) across the whole site
ValidateRequest = false;
base.Execute(requestContext);
}
Just make sure that you are HTML encoding everything that is pumped out to the views that came from user input (it's default behaviour in ASP.NET MVC 3 with Razor anyway, so unless for some bizarre reason you are using Html.Raw() you shouldn't require this feature.
Disable the page validation if you really need the special characters like, >, , <, etc. Then ensure that when the user input is displayed, the data is HTML-encoded.
There is a security vulnerability with the page validation, so it can be bypassed. Also the page validation shouldn't be solely relied on.
See: http://web.archive.org/web/20080913071637/http://www.procheckup.com:80/PDFs/bypassing-dot-NET-ValidateRequest.pdf
I was getting this error too.
In my case, a user entered an accented character á in a Role Name (regarding the ASP.NET membership provider).
I pass the role name to a method to grant Users to that role and the $.ajax post request was failing miserably...
I did this to solve the problem:
Instead of
data: { roleName: '#Model.RoleName', users: users }
Do this
data: { roleName: '#Html.Raw(#Model.RoleName)', users: users }
#Html.Raw did the trick.
I was getting the Role name as HTML value roleName="Cadastro bás". This value with HTML entity á was being blocked by ASP.NET MVC. Now I get the roleName parameter value the way it should be: roleName="Cadastro Básico" and ASP.NET MVC engine won't block the request anymore.
You could also use JavaScript's escape(string) function to replace the special characters. Then server side use Server.URLDecode(string) to switch it back.
This way you don't have to turn off input validation and it will be more clear to other programmers that the string may have HTML content.
I ended up using JavaScript before each postback to check for the characters you didn't want, such as:
<asp:Button runat="server" ID="saveButton" Text="Save" CssClass="saveButton" OnClientClick="return checkFields()" />
function checkFields() {
var tbs = new Array();
tbs = document.getElementsByTagName("input");
var isValid = true;
for (i=0; i<tbs.length; i++) {
if (tbs(i).type == 'text') {
if (tbs(i).value.indexOf('<') != -1 || tbs(i).value.indexOf('>') != -1) {
alert('<> symbols not allowed.');
isValid = false;
}
}
}
return isValid;
}
Granted my page is mostly data entry, and there are very few elements that do postbacks, but at least their data is retained.
You can use something like:
var nvc = Request.Unvalidated().Form;
Later, nvc["yourKey"] should work.
As long as these are only "<" and ">" (and not the double quote itself) characters and you're using them in context like <input value="this" />, you're safe (while for <textarea>this one</textarea> you would be vulnerable of course). That may simplify your situation, but for anything more use one of other posted solutions.
If you're just looking to tell your users that < and > are not to be used BUT, you don't want the entire form processed/posted back (and lose all the input) before-hand could you not simply put in a validator around the field to screen for those (and maybe other potentially dangerous) characters?
You can automatically HTML encode field in custom Model Binder. My solution some different, I put error in ModelState and display error message near the field. It`s easy to modify this code for automatically encode
public class AppModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
try
{
return base.CreateModel(controllerContext, bindingContext, modelType);
}
catch (HttpRequestValidationException e)
{
HandleHttpRequestValidationException(bindingContext, e);
return null; // Encode here
}
}
protected override object GetPropertyValue(ControllerContext controllerContext, ModelBindingContext bindingContext,
PropertyDescriptor propertyDescriptor, IModelBinder propertyBinder)
{
try
{
return base.GetPropertyValue(controllerContext, bindingContext, propertyDescriptor, propertyBinder);
}
catch (HttpRequestValidationException e)
{
HandleHttpRequestValidationException(bindingContext, e);
return null; // Encode here
}
}
protected void HandleHttpRequestValidationException(ModelBindingContext bindingContext, HttpRequestValidationException ex)
{
var valueProviderCollection = bindingContext.ValueProvider as ValueProviderCollection;
if (valueProviderCollection != null)
{
ValueProviderResult valueProviderResult = valueProviderCollection.GetValue(bindingContext.ModelName, skipValidation: true);
bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);
}
string errorMessage = string.Format(CultureInfo.CurrentCulture, "{0} contains invalid symbols: <, &",
bindingContext.ModelMetadata.DisplayName);
bindingContext.ModelState.AddModelError(bindingContext.ModelName, errorMessage);
}
}
In Application_Start:
ModelBinders.Binders.DefaultBinder = new AppModelBinder();
Note that it works only for form fields. Dangerous value not passed to controller model, but stored in ModelState and can be redisplayed on form with error message.
Dangerous chars in URL may be handled this way:
private void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
HttpContext httpContext = HttpContext.Current;
HttpException httpException = exception as HttpException;
if (httpException != null)
{
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
var httpCode = httpException.GetHttpCode();
switch (httpCode)
{
case (int)HttpStatusCode.BadRequest /* 400 */:
if (httpException.Message.Contains("Request.Path"))
{
httpContext.Response.Clear();
RequestContext requestContext = new RequestContext(new HttpContextWrapper(Context), routeData);
requestContext.RouteData.Values["action"] ="InvalidUrl";
requestContext.RouteData.Values["controller"] ="Error";
IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
IController controller = factory.CreateController(requestContext, "Error");
controller.Execute(requestContext);
httpContext.Server.ClearError();
Response.StatusCode = (int)HttpStatusCode.BadRequest /* 400 */;
}
break;
}
}
}
ErrorController:
public class ErrorController : Controller
{
public ActionResult InvalidUrl()
{
return View();
}
}

Resources