I was under the impression that static files (CSS, images, #font-face files, etc) bypassed ASP.NET completely, and were served directly by IIS.
However, my BeginRequest event handler is being called for every HTTP request, including those for static files. This concerns me because I'm creating an Entity Framework data context to be used for the lifetime of each request in that event handler. I don't want to create those contexts if they're never going to be used.
I'm using IIS 7 on Windows 7 Ultimate with no special handler mappings defined. Do I have it wrong? Should these events be firing?
I believe a default ASP.NET MVC site has this set in the web.config.
<modules runAllManagedModulesForAllRequests="true" />
This means every .NET module will be loaded for every IIS request. This is required for ASP.NET MVC to handle extension-less routing. It's essentially a wildcard mapping that you would write in IIS that would match everything and route it to ASP.NET that lives in the web.config.
Read more here, including a way to disable the behavior if you aren't using .NET 4.0. It is nasty, but it's the cleanest solution for sites that can't deal with the overhead of having static files served by asp.net.
BeginRequest will be triggered for all requests (including static content) if:
You're using Visual Studio's development web server.
You've configured IIS to do so.
Please take a look at:
http://forums.asp.net/t/1220664.aspx
In addition to fixing the issue for your static files, you could use Lazy initialization Lazy<T> for your ObjectContext: http://msdn.microsoft.com/en-us/library/dd997286.aspx
The integrated mode in IIS 7 works differently than it was before.
You could switch to classic mode if desired.
Alternatively you can define your custom route handler and do the context initialization there. That way it's only done for specific routes.
Related
I would like to know how the CLR finds the entry point / primary DLL of a web application. How does the CLR know which DLL out of the many that exist in my bin directory, is the DLL for my website?
There is no identifying information in my web.config that specifies one assembly as my "primary" assembly, so how does this selection process occur?
How does this selection / initialization process work?
That depends entirely on if you are using Web Forms, or MVC. The CLR itself does nothing to support this, it's the ASP.NET runtime and IIS that is doing all the leg work.
Web Form
In web forms, your .aspx and "code behind" use class inheritance to define that relationship. The #Page directive has an Inherits attribute defining what class the ASPX will inherit from. The ASPX itself is compiled into a class on-the-fly (or precompiled with aspnet_compiler.exe) that inherits that type.
When IIS hits an ASPX page, it passes that to the PageHandlerFactory either through the Integrated handler, or via the aspnet_isapi handler.
In this case, there is no "startup" DLL, it uses the DLL that the ASPX was compiled against.
MVC
When a request hits the ASP.NET runtime, it looks to see if there is a controller that matches the route. It then uses the DefaultControllerFactory to try and map the route to a controller. That eventually will call GetControllerType to try and find the type for the Controller, which further digs into an internal type called ControllerTypeCache.
ControllerTypeCache does most of the magic. It initialized with the EnsureInitialized, whos job is to populate the list of all types from all assemblies that are referenced using BuildManager.GetReferencedAssemblies. It then crawls each assembly looking for types that match a controller, such as implementing IController, being public, etc.
How does .NET know to fire up the MVC runtime
The MVC runtime is an HttpHandler. Either IIS directly passes it to the MVC handler automatically (newer versions of the framework and IIS do that) or you have to put the MVC handler in the httpHandlers section in the web.config.
In older versions of MVC you had to manually register the MvcHttpHandler. Starting in ASP.NET 4.0, a lot of the URL Routing guts were moved into the ASP.NET framework itself, so it now has this handler out of the box.
The closest thing to a 'startup' DLL in an ASP.Net web app is that assembly that contains `global.asax' (if you have one). See ASP.NET Application Life Cycle Overview for IIS 7.0 for details of what happens when a web app spins up.
You should also bear in mind that there can be a whole stack of HttpModule instances sitting on top of your web app. And your web app can add its own to the stack. The request is handed down through the stack of HttpModules and each of them gets the opportunity to intercept, handle, tamper with your request, etc. And once a response has been made, each module above it in the stack gets the same opportunity to monkey with the response.
I was going to comment on your comment to #VCJones excellent explanation (this stuff usually one just assumes so it's a great learning moment - so kudos to your question as well!).
How then, do we even get into the MVC runtime in the first place?
It's my understanding from reading this MSDN doc that file matching happens first on the requested resource, then route matching (and it's handler - MVC/Web API)
Your question pushed me to tinker :) and fired up a new MVC application with default scaffolding, created a controller for About:
public ActionResult About()
{
return View();
}
and also created a physical file named About.cshtml in the root of the application. I fiddled with the route config so that the controller name wouldn't be part of the route - e.g. http://localhost:123/about (not http://localhost:123/home/about). So now I have
About view
About.cshtml physical file in app root (not the one in /Views/Home/)
Test: http://localhost:123/About
First result: The MVC Controller fired. Wait, I thought it was file matching first?
Then if I navigate to http://localhost:123/About.cshtml a wierd exception is raised: The type of page you have requested is not served because it has been explicitly forbidden. The extension '.cshtml' may be incorrect. What?? Don't I have a ton of cshtml views?
Then I saw this setting in web.config:
<add key="webpages:Enabled" value="false"/> - what if i changed this to true?
Result:
Exception gone (no longer forbidden), and now I can see the behavior as described in the MSDN doc. I can't raise the About view at this point (http://localhost:123/about), because a file exists (and is the response), and I don't get into route matching (so the MVC handler isn't fired).
This is my understanding , so if I'm wrong, I'm sure someone here will point it out and another learning moment will occur :)
So Im reading that:
If a file name extension has not been mapped to ASP.NET, ASP.NET will
not receive the request. This is important to understand for
applications that use ASP.NET authentication. For example, because
.htm files are typically not mapped to ASP.NET, ASP.NET will not
perform authentication or authorization checks on requests for .htm
files. Therefore, even if a file contains only static content, if you
want ASP.NET to check authentication, create the file using a file
name extension mapped to ASP.NET, such as .aspx.
which I understand given the obvious .htm <> ASP.net ISAPI mapping in the webserver but what about routes in a route table? I'm not mapping those in the web server so how does the web server know to forward those URLs to ASP.Net??
So there are at least two pieces to this question:
The first is if you are running in IIS in classic mode versus integrated mode. Classic mode will make things behave like IIS 6, where everything is an ISAPI filter, including ASP.NET itself. Integrated mode takes advantage of the fact that IIS 7 was rewritten from the ground-up and now uses modules instead.
Secondly, the short answer of why IIS knows how to forward a URL to ASP.NET is the Routing Module in the IIS 7+ pipeline; ISAPI filters are now part of the ISAPI Filters Module.
For a visual description of how the IIS 7+ pipeline works from a Routing/URL-Rewriting perspective, read IIS URL Rewriting and ASP.NET Routing
So the good news is if you are very much attached to the ISAPI filter approach you can use the classic mode of IIS.
We have a WCF service in our web application which is being called by browser through ajax(jquery and asp.net scriptmanager)
In this service some of the methods are WebGet and some of them are WebInvoke
Problem is now any request to WebInvoke method through asp.net scriptmanager is making request using http 'OPTIONS' instead of 'POST'. And webserver is going to deny this request as it don't allow OPTIONS
POST should be default for WebInvoke attribute when no method is defined.
We started having this problem since last couple of weeks just spotted today. We upgraded everything to .net 4.0 around same time so not sure .net 4.0 has something changed.
Also weird thing is everything works fine when using website like xyz.com we only see problem when using www.xyz.com
Any idea what it could be?
Edit: Ok got little closure
Problem is Asp.net scriptmanager does not allow cross site reference in ajax.
But wait we do not do anything that calls cross site.
What i found is when i use www.a.com some how js files generated by script manager wants to use a.com not www.a.com.
Any idea how to resolve this?
We configured IIS 5 by mappping * to asp.net handler so that ASP.NET MVC works. After configuring this, directory browsing is not working.
Also uploadify jquery plugin is not working. Showing IO error 2038.
Can someone please suggest us how to enable directory browsig with ASP.NET MVC configurations on IIS 5?
I don't think that you'll be able to get directory browsing to work with ASP.NET MVC in the same application. When you added the wildcard mapping, you told ASP to handle every request. If the request doesn't map to an actual file, it will try to match a route in MVC. If there isn't a matching controller with an index (assuming that's your default) action, then it will fail.
My suggestion is to split your web site into "application" and "content". Set up the application as a separate web site and apply the wildcard mapping there. Leave your content with the original configuration. I don't use IIS5 any more -- with one exception on an old XP development box -- I'm afraid that can't really be of more help.
If I were you, though, I'd upgrade to a more recent OS and web server. Expecting new technology to work on a decade-old platform is very optimistic.
I have a ASP.NET MVC app on IIS7 using Forms Authentication in Integrated Mode. I am noticing that the ASP.NET runtime is being hit for every request that comes in even if it is only for static files (probably because of Integrated Mode). Is there a way to configure IIS7 to serve up static files without hitting ASP.NET?
I've been thinking that the only way around this is to create a separate virtual directory just for static files -- a mini-CDN, if you will.
Any other ideas?
To avoid having your HttpModule called for static files, configure it in web.config to use preCondition="managedHandler".
In case it helps, event handlers in Global.asax are not called for static files.
Also, be aware that all HttpModules are called for all files when you're testing with Cassini.