How to define error-handling logic at the server level - asp.net

For a website/ vpath, it's possible to handle the Application_Error event to catch errors before they get sent back to the browser. Is it possible to also do this at the server level somehow? That is, define a method at the root level that will execute if an error occurs in a website, but that website fails to handle the error for whatever reason.
I know you can use the web.config at the root level to define custom error messages per HTTP status code. However, this isn't ideal for my case, because I want to return different types of content (ie, HTML or something else) depending on the application logic.

A custom http module can be registered at applicationHost.config. Then this module is used by all IIS applications on the target machine.
1) Create a signed class library project with http module:
public class ErrorHandlingModule : IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication context)
{
context.Error += new EventHandler(context_Error);
}
void context_Error(object sender, EventArgs e)
{
// handle error
}
}
2) Install the class library into GAC, so it can be shared by all IIS applications.
3) Install the http module to applicationHost.config file. This file usualy resides in C:\Windows\System32\inetsrv\config. Files in this folder can be accessed only by 64-bit processes (there is no such issue on 32-bit OSes), VS2010 cannot see them but Explorer can. The applicationHost.config fragment could look like this:
<location path="" overrideMode="Allow">
<system.webServer>
<modules>
<add name="MyModule" preCondition="managedHandler" type="GlobalErrorHandler.ErrorHandlingModule, GlobalErrorHandler, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bfd166351ed997df" />

I am not clear what your question is but as per my understanding. inside application_error you can use ,
Server.GetLastError() to get last error occured in server level.

Related

httpmodules(Classic Mode) Vs Modules (Integrated mode) not parsing request in the same way

I have an ASP.NET FW 4.5 application, normally it is running under Classic Mode , and we use a HttpModule that parse every request to webpages.
The Module checks in the DB if the user has access.
No problem with that.
When we switch to integrated mode we put a < webserver > < module > as recommended in the migration guide.
The problem is that the module is parsing every request to the application, also the css, jss and images.
In Classic mode this does not happen, only pages are parsed.
Is there a different behavior between classic and integrated?
The module implements methods like:
private void OnBeginRequest(object sender, EventArgs e)
private void OnAuthorization(object sender, EventArgs e)
This question is a bit old but just in case somebody else finds it I'll provide the answer...
In integrated mode, any modules you provide in <system.webserver> are invoked for every IIS request (including non ASP.NET pages) unless you add a constraint via the preCondition attribute. e.g.
<system.webserver>
<modules>
<add preCondition="managedHandler" name="..." type="..."/>
</modules>
</system.webserver>
Specifying managedHandler means the module will be invoked only for ASP.Net managed resources such as .aspx files, but not unmanaged resources such as html, images and javascript.
Note, however, this setting is overridden if you specify runAllManagedModulesForAllRequests="true" on the <modules> element, which causes all modules (and your Global.asax class if present) to be notified about all requests.
There's a useful write-up of the Life Cycle of an IIS 7.x request on MSDN, but this does not mention the preCondition attribute. However you can read about it in the IIS Settings Schema documentation.
TL;DR
You might be wondering how it's possible that a module can be invoked for a non-managed resource when the event handlers defined in your Global.asax file are not invoked. After all, modules register their event handlers with the HttpApplication object that is passed to the IHttpModule.Init method like this:-
public void Init(System.Web.HttpApplication context)
{
context.AuthenticateRequest += my_request_handler;
}
The HttpApplication passed to Init is the same as that defined in Global.asax, so why aren't the Global application event handlers invoked? The answer is simply that when a module registers its event handlers with the HttpApplication object, the HttpApplication is aware that it is in module initialization mode, and registers the event handlers separately, along with flags to indicate if the event handler should be called for non-managed resources. You can investigate further by looking at the
HttpApplication reference source code.

Subscribing to HttpApplication events inside HttpModules behave differently between Integrated and Classic mode

I noticed some strange behavior regarding the events raised by the same HttpApplication instance and the HttpModules that subscribed the same eventhandler during their initialization, and this between IntegratedMode and ClassicMode in IIS 7 (at least up to version 8).
It seems that when your modules subscribe event handlers to the given HttpApplication instance in their Init() method, when running in IntegratedMode, that the C# rules related to subscribing to events don't apply anymore, at least when the events are being raised.
Normally when you do a subscription like this
httpApplication.EndRequest -= SomeMethod;
httpApplication.EndRequest += SomeMethod;
You are garantueed that SomeMethod is only subscribed once and therefore when the httpApplication's EndRequest is raised, your method will only be called once.
Of course if SomeMethod is an instance method and you have multiple instances, then each instance will have its method called, that is normal. But if you have a static method, then no matter how many different instances subscribe that same static SomeMethod it should only be called once.
And that does not seem to be the case when you have multiple HttpModule instances that subscribe the same static method to the same event of the same HttpApplication instance, while running in IntegratedMode
If you decompile the HttpApplication code then you see that each event subscription is actually translated to some notification in IIS (at least when running in IntegratedMode). That is all fine, but I have the feeling that they made the assumption that each event handler being attached to the HttpApplication instance events during the Init() method of the HttpModule is supposed to be an instance method of that specific HttpModule which is strange to say the least?
Below you'll find a reproducing sample as small as possible that clearly reflects this issue. I'm not looking for other ways to create the sample (to bypass the issue, restructure the code, ...), it simply reproduces the issue with the least amount of code and setup.
So my question is:
Is this strange behavior by design or is it something they overlooked/a bug? Or do I make the wrong assumptions with regard to the subscriptions?
Steps to reproduce
Create an empty web application project named IssueDemo and include the following files into it
Add an empty Index.aspx page (I'm using a webform page, but the same issue is there for MVC...)
Add a Modules.cs file with the following content
namespace IssueDemo
{
public abstract class ModuleBase : System.Web.IHttpModule
{
public void Init(System.Web.HttpApplication context)
{
System.IO.File.AppendAllText(
System.AppDomain.CurrentDomain.BaseDirectory + "trace.log",
string.Format("Init() called on {0} (#{1}) for HttpApplication (#{2}){3}",
this.GetType(),
this.GetHashCode(),
context.GetHashCode(),
System.Environment.NewLine));
context.EndRequest -= LogSomething;
context.EndRequest += LogSomething;
}
public void Dispose() { }
private static void LogSomething(object sender, System.EventArgs e)
{
System.Web.HttpApplication httpApplication = (System.Web.HttpApplication)sender;
System.IO.File.AppendAllText(
System.AppDomain.CurrentDomain.BaseDirectory + "trace.log",
string.Format("LogSomething() called on ModuleBase triggered by event raise of HttpApplication (#{0}) for Request (#{1}): {2}{3}",
httpApplication.GetHashCode(),
httpApplication.Request.GetHashCode(),
httpApplication.Request.RawUrl,
System.Environment.NewLine));
httpApplication.Response.Write("Written by ModuleBase's LogSomething()<br/>");
}
}
public class MyHttpModule : ModuleBase { }
public class MyOtherHttpModule : ModuleBase { }
}
Adapt the web.config file to reflect the following content (beware of the assembly reference if you did not name your project IssueDemo)
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<httpModules>
<add name="MyHttpModule" type="IssueDemo.MyHttpModule, IssueDemo"/>
<add name="MyOtherHttpModule" type="IssueDemo.MyOtherHttpModule, IssueDemo"/>
</httpModules>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules>
<add name="MyHttpModule" type="IssueDemo.MyHttpModule, IssueDemo" />
<add name="MyOtherHttpModule" type="IssueDemo.MyOtherHttpModule, IssueDemo" />
</modules>
</system.webServer>
</configuration>
Run it in ClassicMode (you can use the built-in VS Development Server or choose the Classic .Net AppPool in IIS for your application). You'll see the following message in the browser:
Written by ModuleBase's LogSomething()
and the trace.log file will show the following content (I removed the entries for favicon.ico and instance ids will differ):
Init() called on IssueDemo.MyHttpModule (#9654443) for HttpApplication (#11543392)
Init() called on IssueDemo.MyOtherHttpModule (#66322936) for HttpApplication (#11543392)
LogSomething() called on ModuleBase triggered by event raise of HttpApplication (#11543392) for Request (#19612087): /index.aspx
which is basically what I would expect when subscribing the same static method to the same HttpApplication instance (#11543392).
Run it in IntgratedMode (you can't use the built-n VS Development Server but you can use IISExpress or normal IIS with DefaultAppPool). You'll now see the following messages in the browser:
Written by ModuleBase's LogSomething()
Written by ModuleBase's LogSomething()
and the trace.log file will show the following content (I removed the entries for favicon.ico and the creation of other httpApplication instances, and instance ids will differ):
Init() called on IssueDemo.MyHttpModule (#39086322) for HttpApplication (#36181605)
Init() called on IssueDemo.MyOtherHttpModule (#28068188) for HttpApplication (#36181605)
LogSomething() called on ModuleBase triggered by event raise of HttpApplication (#36181605) for Request (#63238509): /index.aspx
LogSomething() called on ModuleBase triggered by event raise of HttpApplication (#36181605) for Request (#63238509): /index.aspx
which is not what I expected when subscribing the same static method to the same HttpApplication instance (#36181605). You see the duplicate execution for the same request (#63238509), and since there is only event handler attached, the only conclusion I can make is that the event is raised twice. By the way if you add some more derived types and register them in the web.config, you'll see that the duplications will increase (but only in IntegratedMode).
If anybody can answer this, that would be great. In the meanwhile I already worked around this issue by checking if our code already executed during a specific request.
After a few days I asked the same question on Microsoft Connect and today they provided the following answer: This behavior is by design and deals with the difference in how modules are registered between classic mode and integrated mode
Below you find the elaboration on their answer as well:
In classic mode, IIS effectively sees the entire managed ASP.NET application (both the System.Web runtime and any custom modules which are registered) as one single HTTP module. IIS simply notifies ASP.NET that it should perform some work, and the ASP.NET runtime will go through its list of modules and kick everything off one by one. Since ASP.NET is entirely responsible for event management, we just use a single EventHandlerList (per HttpApplication) to coordinate everything.
In integrated mode, however, IIS is aware of every single individual module running in the pipeline. IIS coordinates which modules are to receive which notifications. Once a module's Init method has run to completion, its registrations are considered "baked" for the lifetime of the module. This implies that the module event registrations are segregated: each module is given its own independent event handler registration object (since IIS internally keeps them segregated). If Module A calls add_EndRequest with some delegate and Module B calls remove_EndRequest with the same delegate, the event handler store is actually being backed by two different objects in memory, so Modules A and B cannot influence each others' registrations.

Weird "The file '/DefaultWsdlHelpGenerator.aspx' does not exist" error when remapping WebService HttpHandler

I have dynamic CMS-driven (custom rolled, I know, wheels, etc. but not my decision!) site, which uses an HttpModule to direct content. I have found that .asmx resources were not working. After investigation, I figured out that this was because I had essentially overridden the handler by taking the request out of the overall pipeline.
So I am now detecting if the resource exists and is an .asmx file, and handling accordingly. Which I think is to create a WebServiceHandler using WebServiceHandlerFactory and then remapping it.
This works fine with a ?wsdl querystring, but ask for the URI itself and you get (at point indicated by asterisks):
System.InvalidOperationException was unhandled by user code
Message=Failed to handle request.
[snip]
InnerException:
System.InvalidOperationException
Message=Unable to handle request.
Source=System.Web.Services
InnerException: System.Web.HttpException
Message=The file '/DefaultWsdlHelpGenerator.aspx' does not
exist.
Note the final InnerException. This thread appears to suggest a corrupt .NET Framework install, but the file is present in the 4.0 Config folder. I suspect a mistake on my part. Am I remapping incorrectly?
public class xxxVirtualContentHttpModule : xxxHttpModule
{
protected override void OnBeginRequest(IxxxContextProvider cmsContext, HttpContext httpContext)
{
string resolvePath = httpContext.Request.Url.AbsolutePath;
// is path a physical file?
IRootPathResolver rootPathResolver=new HttpServerRootPathResolver(httpContext.Server);
string serverPath = rootPathResolver.ResolveRoot("~" + resolvePath);
if (File.Exists(serverPath))
{
if (Path.GetExtension(serverPath).Equals(".asmx", StringComparison.CurrentCultureIgnoreCase))
{
WebServiceHandlerFactory webServiceHandlerFactory = new WebServiceHandlerFactory();
IHttpHandler webServiceHttpHandler = webServiceHandlerFactory.GetHandler(httpContext, "Get", resolvePath, serverPath); // *****
httpContext.RemapHandler(webServiceHttpHandler);
}
}
}
Update
I have removed all references to the HttpModules and this issue still occurs, meaning it has nothing to do with the CMS portion of the system.
Solved it.
There seems to be a new configuration added to web.config:
<system.web>
<webServices>
<wsdlHelpGenerator href="DefaultWsdlHelpGenerator.aspx" />
</webServices>
</system.web>
Removed this, and it all works.

WCF - Using ServiceRoutes instead of svc files -- My app states I need AspNetCompatability only when I first attempt to connect?

public class Global : HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
RegisterRoutes(RouteTable.Routes);
}
private static void RegisterRoutes(ICollection<RouteBase> routes)
{
routes.Add(new ServiceRoute("Calculator", new WebServiceHostFactory(), typeof(CalculatorService)));
}
}
<system.serviceModel>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" aspNetCompatibilityEnabled="true">
<serviceActivations>
<add factory="System.ServiceModel.Activation.ServiceHostFactory"
relativeAddress="Calculator.svc"
service="MyServer.CalculatorService"/>
</serviceActivations>
</serviceHostingEnvironment>
</system.serviceModel>
When I do this and go to http://localhost/MyApp/Calculator.svc I get an error saying I need AspNetCompatability. So I added [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] and it works, the only thing is I'm worried that I will want my service to use alternative means of transport (not just http) if I want to get into using alternative non-http bindings.
The strange thing is that if I don't set the attribute to Allowed or Required then when I rebuild my page I get that error. After I get that error I hit refresh and everything is fine. And it isn't just if I query the svc through a web browser, but if I have an app it crashes the first time it connects (if the server was restarted) and afterwards it works. What gives?
I think that your problem is messed configuration. You are adding a route and in the same time you are registering the service with configuration based activation. Use either one or second. Also you can use routes and only Http based protocols or non-http protocols but without routes.
Unfortunately, yes, you must enable ASP.NET compatibility to use ServiceRoutes. This is because the ASP.NET runtime is now responsible for routing the traffic instead of just IIS modules.

different web.config settings for http and https

Is it possible to configure your web.config file of your asp.net website to use different settings for users accessing the site via HTTPS?
(eg. I need to have validateRequest attribute for https access set to false, but for internal access (using http) set to true...)
thanks!
For security reasons, I would recommend deploying internal and extenal sites a different sites altogether. That means you could use windows authentication internally and forms authentication externally, and change whatever other config settings you desire. It also means you can limit the attack surface for external evil-doers by not providing access to methods intended for internal users only.
Disable request validation in the Web.config file:
<system.web>
<pages validateRequest="false"/>
</system.web>
And in the Global.asax file, add an event handler for BeginRequest along the lines of:
public class Global : HttpApplication
{
public override Init()
{
base.Init();
BeginRequest += ToggleValidation;
}
void ToggleValidation(object sender, EventArgs e)
{
if (Request.IsSecureConnection)
Request.ValidateInput();
}
}

Resources