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.
Related
I have a Global.asx file that needs to do custom authentication, auditing and profiling stuff. This is needed because it supports a SAML based SSO system and needs to override the normal .Net authentication (which doesn't support either SAML or mixed authentication)
I don't want to fire it for static files, such as .js, .css, .png, etc
In Cassini/WebDev and IIS7 it does.
What I want to have is some simple check, like a this.Request.IsStaticFile (which doesn't exist, unfortunately) to identify the static files.
I realise that this would be fairly simple to write, but it feels like something that must already exist - IIS has already applied caching policy stuff for the static files and so on.
I need a code solution, rather than an IIS config change one.
Update
This is my current workaround:
/// <summary>Hold all the extensions we treat as static</summary>
static HashSet<string> allowedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
".js", ".css", ".png", ...
};
/// <summary>Is this a request for a static file?</summary>
/// <param name="request">The HTTP request instance to extend.</param>
/// <returns>True if the request is for a static file on disk, false otherwise.</returns>
public static bool IsStaticFile(this HttpRequest request)
{
string fileOnDisk = request.PhysicalPath;
if (string.IsNullOrEmpty(fileOnDisk))
{
return false;
}
string extension = Path.GetExtension(fileOnDisk);
return allowedExtensions.Contains(extension);
}
This works and is quick enough, but feels horribly clunky. In particular relying on extensions is going to be error prone if we add new static files not thought of.
Is there a better way without changing the IIS config?
You might be able to check which handler is dealing with the request.
In IIS6 only .net files, eg aspx are mapped to a handler that does stuff.
In IIS7 with the integrated pipeline, everything routes through .net, which is normally a good thing. Different handlers still deal with different file types though. In particular I believe the staticfilehandler is the one you need to check for. The httpcontext.handler property should allow you to figure it out.
You could create an extension method to add that IsStatic method...
Simon
There are a few options:
Adding authorization element and deny none for those paths that you do not need any authentication and contains your static files
You are using integrated pipeline. Turn it off on your IIS 7.
There is no doubt that you need to create a custom extension method because ASP.NET routing engine uses this code to decide whether a file exist,
if (!this.RouteExistingFiles)
{
string appRelativeCurrentExecutionFilePath = httpContext.Request.AppRelativeCurrentExecutionFilePath;
if (((appRelativeCurrentExecutionFilePath != "~/") && (this._vpp != null)) && (this._vpp.FileExists(appRelativeCurrentExecutionFilePath) || this._vpp.DirectoryExists(appRelativeCurrentExecutionFilePath)))
{
return null;
}
}
You will not able to decide whether the request is static in Application_BeginRequest using context.handler because Routing Module may change the handler and this module always execute after Application_BeginRequest. My suggestion is to use the similar code which ASP.NEt routing engine uses.
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.
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
}
...
}
In my asp.net mvc 2 app, I'm wondering about the best way to implement this:
For every incoming request I need to perform custom authorization before allowing the file to be served. (This is based on headers and contents of the querystring. If you're familiar with how Amazon S3 does rest authentication - exactly that).
I'd like to do this in the most perfomant way possible, which probably means as light a touch as possible, with IIS doing as much of the actual work as possible.
The service will need to handle GET requests, as well as writing new files coming in via POST/PUT requests.
The requests are for an abitrary file, so it could be:
GET http://storage.foo.com/bla/egg/foo18/something.bin
POST http://storage.foo.com/else.txt
Right now I've half implemented it using an IHttpHandler which handles all routes (with routes.RouteExistingFiles = true), but not sure if that's the best, or if I should be hooking into the lifecycle somewhere else?
I'm also interested in supporting partial downloads with the Range header. Using
response.TransmitFile(finalPath);
as I am now means I'll have to do that manually, which seems a bit lowlevel?
Many thanks for any pointers.
(IIS7)
I think having a custom handler in the middle that takes care of this is exactly how you should be doing it.
TransmitFile is the lightest-weight programmatic way to serve a file that I am aware of.
However, you might not need to write your own HttpHandler. You can use the MVC handler and just dedicate a controller action to the job. Something like:
http://storage.foo.com/Files/Download/SomeFileIdentifier
...routing to...
public FilesController
{
public ActionResult Download(string id)
{
//...some logic to authenticate and to get the local file path
return File(theLocalFilePath, mimeType);
}
}
The File() method of controller uses TransmitFile in the background, I believe.
(PS, If you want shorter URLs, do it via custom routes in global.asax.)
I need to implement a custom handler for MVC that gives me the first look at URLs requests to determine if it should rewrite the urls before submitting the URL to the routing engine. Any pattern is a candidate for the redirect, so I need to intercept the URL request before the standard MVC routing engine takes a look at it.
After looking at a whole bunch of examples, blogs, articles, etc. of implementing custom routing for ASP.NET MVC, I still haven't found a use-case that fits my scenario. We have an existing implementation for ASP.NET that works fine, but we're returning the "standard" handler when no overrides are matched. The technique we're currently using is very similar to that described in this MSDN article: http://msdn.microsoft.com/en-us/library/ms972974.aspx#urlrewriting_topic5 which says "the HTTP handler factory can return the HTTP handler returned by the System.Web.UI.PageParser class's GetCompiledPageInstance() method. (This is the same technique by which the built-in ASP.NET Web page HTTP handler factory, PageHandlerFactory, works.)".
What I'm trying to figure out is: how can I get the first look at the incoming request, then pass it to the MVC routing if the current request doesn't match any of the dynamically configured (via a data table) values?
You would need to:
Not use the standard MapRoute extension method in global.asax (this is what sets up the route handler).
Instead, write your own route subtype, like this.
Although I explained to Craig I may not need to override scenario any more (favoring the future rather than the past), I realized there is an easy and reasonably clean place this override could be implemented - in the Default.aspx code behind file. Here's the standard Page_Load method:
public void Page_Load(object sender, System.EventArgs e)
{
// Change the current path so that the Routing handler can correctly interpret
// the request, then restore the original path so that the OutputCache module
// can correctly process the response (if caching is enabled).
string originalPath = Request.Path;
HttpContext.Current.RewritePath(Request.ApplicationPath, false);
IHttpHandler httpHandler = new MvcHttpHandler();
httpHandler.ProcessRequest(HttpContext.Current);
HttpContext.Current.RewritePath(originalPath, false);
}
As you can see, it would be very easy to stick a url processing interceptor in front of the MVC handler. This changes the standard default page behavior and would need to be reflected in any other MVC app you'd want to create with this same method, but it sure looks like it would work.