I have an Asp.Net 4.0 Web Forms application that throws following error certain times
“Server Error in ‘/MySiteDev’ Application” .
This error comes only at times. And this error is not firing the Application_Error event which is handled in Global.asax.
Since this is not firing Application_Error, what all are the other possible places that will have a log of this error event? Anything other than event viewer available?
Any way to find out the exceptions handled by the ASP.Net framework?
Note: customErrors mode="Off". Also runAllManagedModulesForAllRequests="true"
UPDATE
Reference from How to: Handle Application-Level Errors
An error handler that is defined in the Global.asax file will only catch errors that occur during processing of requests by the ASP.NET runtime. For example, it will catch the error if a user requests an .aspx file that does not occur in your application. However, it does not catch the error if a user requests a nonexistent .htm file. For non-ASP.NET errors, you can create a custom handler in Internet Information Services (IIS). The custom handler will also not be called for server-level errors.
You cannot directly output error information for requests from the Global.asax file; you must transfer control to another page, typically a Web Forms page. When transferring control to another page, use Transfer method. This preserves the current context so that you can get error information from the GetLastError method.
After handling an error, you must clear it by calling the ClearError method of the Server object (HttpServerUtility class).
CODE
protected void Application_Error(object sender, EventArgs e)
{
//Get the exception object
Exception exception = Server.GetLastError().GetBaseException();
//Get the location of the exception
string location = Request.Url.ToString();
if (!String.IsNullOrEmpty(location))
{
string[] partsOfLocation = location.Split('/');
if (partsOfLocation != null)
{
if (partsOfLocation.Length > 0)
{
location = partsOfLocation[partsOfLocation.Length - 1];
}
}
//Maximum allowed length for location is 255
if (location.Length > 255)
{
location = location.Substring(0, 254);
}
}
string connectionString = ConfigurationManager.ConnectionStrings[UIConstants.PayrollSQLConnection].ConnectionString;
ExceptionBL exceptionBL = new ExceptionBL(connectionString);
exceptionBL.SubmitException(exception.Message, location);
Log.Logger.Error(exception.Message);
}
CONFIG
<system.web>
<compilation debug="true" targetFramework="4.0" />
<pages validateRequest="false"></pages>
<httpRuntime requestValidationMode="2.0" />
<customErrors mode="Off"/>
<authentication mode="Windows"></authentication>
<identity impersonate="true" userName="domain\xxxx" password="xxxx"/>
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
<httpErrors errorMode="Detailed" />
</system.webServer>
UPDATED REFERENCES
Application_Error not firing
Global.asax event Application_Error is not firing
Application_Error does not fire?
How to: Handle Application-Level Errors
Examine logs in the Event Viewer, which should log both server-level and application-level errors.
The application error handler probably isn't processing the event because it's happening before your application is successfully started and a context created. So, there would seem to be an application configuration or server configuration ceasing processing the request.
Or, the application is encountering a problem early enough in the request lifecycle that even after starting, it's 'hung' until the server decides to kill the process (for instance, perhaps in the form of a StackOverflowException mentioned by #MikeSmithDev).
This is a load balanced environment with A and B boxes.
The team who deployed the web application confirmed that in one of the boxes, the config file as not copied properly.
I think, the application worked well when it was hitting A box and failing in B box. I think, since the config is not there, it was not possible to call Application_Error.
Please tell if you have different opinion.
Note: The issue is not there when they re-deployed
Related
I have a Web Form (.NET 4.0) web application built as:
MyApp.Portal (Main portal with the pages, WCF AJAX services, scripts etc.)
MyApp.BusinessLayer (Class library call DataAccess to get data)
MyApp.Services (Class library contains WCF server for server side code)
MyApp.DataAccess (ADO Oracle)
I am trying to following some tutorials on error handling but so far I am getting inconsistent results.
My question is how should I be handling errors in my app?
Should I be wrapping all methods in try/catch?
Should I just wrap the methods in the business layer with the try/catch?
How would I handle errors that occur in the PortalLayer with the WCF Ajax methods?
For example I added the following to my Global.asax file:
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
Exception exc = Server.GetLastError();
if (exc is HttpUnhandledException)
{
if (exc.InnerException != null)
{
exc = new Exception(exc.InnerException.Message);
Server.Transfer(#"\Pages\Error.aspx?handler=Application_Error%20-%20Global.asax", true);
}
}
}
However most of the time this isnt getting called. I just see error an error message show on the screen (Like a glorified alert). Or if this method is called then exc is not HttpUnhandledException so the transfer never takes place.
I also tried this in my web.config but I don't see that this does anything. (If I comment it out I get the same results)
<customErrors mode="On" defaultRedirect="Error.aspx?handler=customErrors%20section%20-%20Web.config">
<error statusCode="404" redirect="ErrorPage.aspx?msg=404&handler=customErrors%20section%20-%20Web.config"/>
</customErrors>
The ultimate goal is to redirect the user to a page (or maybe just nicely display the error) while out the same time either log it in a file or write the error out to a database.
The void Application_Error(object sender, EventArgs e) will get called for exceptions that happen in the main thread when server is processing pages (request to .aspx,.ashx resource).
It won't get called in two scenarios:
you're firing new thread when processing .aspx resource & the exception will happen in that new thread
in web service methods, wcf methods, etc. (i.e. non-.aspx resource)
exception is "bad ass" exception - the kind that corrupts application domain - StackOverflowException or OutOfMemoryException
So for WCF, WebServices it makes sense to wrap all entry points in
try {}
catch(Exception e) {
Logger.Log(e);
throw;
}
statement (note if you have lot of those, invest your time in more generic solution).
Also note - if there is an error on your error page you'll get yellow screen of death (obviously). So I prefer showing static html pages as error page (lowest probability that something will go wrong).
In your error handling code you're not cleaning up the error state, also I'd prefer Redirect to Server.Transfer for error handling. Final snipped looks like:
var ex = Server.GetLastError();
Logger.Log(ex);
Server.ClearError();
Response.Redirect("/error.aspx");
Final note - no need to do error logging by yourself (good programmers are lazy - in terms of "don't reinvent the wheel) - there are plenty of great logging modules like Elmah, Microsoft Enterprise Library or log4net.
I have a few http handlers (IHttpHandler) in my asp.net web project. Now I want to restrict access to these handlers. For Handler1 I want to allow only POST requests, and for Handler2 I want to allow only GET requests.
In my web.config I modified the <httpHandlers> section as shown below, but both handlers still process all verb types. Is there something I've missed? I'm testing it using IIS Express.
<httpHandlers>
<add verb="POST" path="Handler1.ashx" type="MyNamesapce.Handler1, MyAssembly"/>
<add verb="GET" path="Handler2.ashx" type="MyNamesapce.Handler2, MyAssembly"/>
</httpHandlers>
The reason this isn't working for you is that you've conflated two slightly different "flavours" of something that implements IHttpHandler.
There are two ways that you can implement an IHttpHandler with asp.net:
Create a class that implements IHttpHandler, e.g. MyCustomHandler.cs. This type of handler won't respond to any requests without being configured in your web.config file.
Create an .ashx file (which it looks like you've done), e.g. MyOtherHandler.ashx. This type of handler will respond to any requests to its URL, e.g. http://localhost/MyOtherHandler.ashx
The first type requires entries in the web.config file to work, the second doesn't. This is why you're seeing your .ashx handlers responding to all HTTP verbs, because they're being handled by the part of the asp.net framework that responds to requests for .ashx files, rather than being triggered by your web.config file. If you're using IIS Express, you can see this configured in the file %USERPROFILE%\Documents\IISExpress\config\applicationhost.config. Search for ".ashx" and you'll find a line similar to the below in the <system.webServer><handlers> section:
<add name="SimpleHandlerFactory-Integrated-4.0" path="*.ashx"
verb="GET,HEAD,POST,DEBUG" type="System.Web.UI.SimpleHandlerFactory"
preCondition="integratedMode,runtimeVersionv4.0" />
This is equivalent to what you've been adding to your web.config, but is responsible for telling IIS/asp.net "respond to any URLs that end in .ashx with any of the listed verbs by having the code in the type System.Web.UI.SimpleHandlerFactory deal with it. This code then loads your .ashx file.
To create a handler that can respond to any address you choose, you need (in short) a .cs file containing something similar to:
using System.Web;
namespace HttpHandlers
{
public class Handler4 : IHttpHandler
{
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
context.Response.Write("Hello World from Handler4.cs");
}
}
}
You can then wire it into your web.config file with:
<add name="Handler4" verb="POST" path="Handler4.ashx" type="HttpHandlers.Handler4, HttpHandlers" />
NOTE: My project that I created to test this is called "HttpHandlers", hence the type declaration that I've specified in that web.config snippet.
I have an HTTPModule:
public class MyErrorModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.Error += new EventHandler(Application_Error);
}
public void Application_Error(object sender, EventArgs e)
{
// Handle error
}
}
It is declared (IIS7.5 Integrated) in web.config:
<system.webServer>
<!-- some more stuff -->
<modules runAllManagedModulesForAllRequests="true">
<add name="ApplicationErrorModule" preCondition="" type="xxxxx"/>
</modules>
<!-- some more stuff -->
</system.webServer>
I tried without the runAllManagedModulesForAllRequests="true" too
I have a WebMethod in a normal webs.aspx.cs (it is part of a big project and I hope I don't have to move everything in an asmx...):
[WebMethod]
public static Model.Response GetDetails(string email)
{
// Do some stuff and return JSON data
}
I call using JQuery and AJAX to POST
https://servername/webs.aspx/GetDetails
In normal situation (without unhandled Exception) things are fine, web-service return what I need. But there are cases I need to call code in a DLL that can do a
Context.Redirect(page,true);
which generates a ThreadAbortException
I was hoping that the HttpModule would help but when the THreadAbortException occurs it doesn't go in the module.
I followed some suggestions to use
<customErrors mode="Off" />
but it doesn't help.
Where is it caught by ASP.Net? I am using 4.0. How can I capture the exception in the Handler?
I see a jsonerror=true in the HTTP Response Header, which makes me thing there is another place it is caught. I don't see other custom module that would catch the exception.
UPDATE
I tried with a webs.asmx webservice with proper annotation (ScriptService etc...) and I still can't capture the ThreadAbortException
I am doing a system version comparison during OnApplicationStarted method in Global.asax which does not allow the system boot up if the database version and system version are not matching.
So it looks like this:
protected override void OnApplicationStarted()
{
try{
if(systemVersion != dbVersion)
throw new Exception("They are not same!");
}
catch{
//do some other things
//Response.Redirect("~/VersionError.html");
}
}
but it says "Response is not available in this context." I tried to catch the error in Application_Error but I got the same error.
My question is that how I can redirect users to an error page from within these methods?
Edit:
I know there is no Response at this time but I was asking how to get around this problem. And also one of the reasons why I can hit methods after OnApplicationStarted is that we don't want to load many things if this exception occurs.
Since you don't have access to the Response when the application starts but want all incoming requests to go to an error page if a condition is met, you need to set up something farther up the pipeline. Think IIS modules. Install a module via the following:
<system.webServer>
<modules>
<add name="VersionsNotSameHandler"
type="Company.Exceptions.VersionsNotSameHandler, Company.Exceptions"
preCondition="managedHandler" />
</modules>
</system.webServer>
Now for the module code:
namespace Company.Exceptions
{
public class VersionsNotSameHandler: IHttpModule
{
public void Dispose() { }
public void Init(HttpApplication application)
{
context.PreRequestHandlerExecute += newEventHandler(OnPreRequestHandlerExecute)
}
public void OnPreRequestHandlerExecute(Object source, EventArgs e){
HttpApplication app = (HttpApplication)source;
HttpRequest request = app.Context.Request;
// Check for invalid versioning
app.Response.Redirect('.html page displaying error', true);
}
}
}
The OnPreRequest occurs before every request, making sure that the versions are always checked...you may want to cache if you notice things slowing down.
There is no response at this point you are in application start! This is when the application is starting not when a response is being made.
You could set something in here to direct every url to an error page if this condition is met by setting up a default route which will catch all requests. This would kind of give you the functionality you want as future requests would then get an error page.
Can you just throw an HTTP error:
throw new HttpException(403, "Forbidden");
And then in your web.config:
<customErrors mode="On">
<error statusCode="403" redirect="~/VersionError.html"/>
</customErrors>
I am trying to show a customer errors page in ASP.NET when the database is down. I use the SQL Server mode to hold the session data. The problem is that the custom errors page is never called.
Since the session data and the database are on the same server, this does not redirect to the custom error page? I’m guessing the web application has not loaded at this point?. The user is presented with the stack trace for the session state connection failure.
It seems that we need something that sits in front of the initial website load to check connectivity to the database. Any ideas on how to implement this?
Add something like this to your web.config?
<customErrors mode="RemoteOnly" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="NoAccess.htm" />
<error statusCode="404" redirect="FileNotFound.htm" />
</customErrors>
You can read more information here
If it is your SqlSessionState that is failing, you should handle the corresponding error in the Application_Error event in Global.asax
You can read more information here
I believe the error is coming from the fact that because you're using an out of memory session state provider (being the database), and the database connection has a failure, then there is actually a perceived error in the web configuration (not in the application). I have a similar problem, where I'm using AppFabric Cache for my session state provider, but when the AppFabric Cache Service is down, I get the Configuration Error page.
Because of this, you can't use the customErrors solution as FlyingStreudel has already suggested, since it isn't an error in your application, but rather in the loading of the configuration.
I've looked around for a way to solve this, but couldn't find any. I hope this question gets answered, it's got me confused already with the various error configuration options...
Update: After investigating this for a while now, it appears that my issue comes from the fact that the SessionStateModule causes the AppFabric cache session state provider to try and connect to the DataCache (which is not available), and an exception (probably timeout) is thrown somewhere. Because this happens in the Init of the HTTP module, there seems to be no way around the yellow screen of death.
I wouldn't be surprised if the original poster's problem is the same - the connection to the SQL server occurring in the initialization of the SessionStateModule.
Because the error page is an ASP.NET Page ( I can see this from your comment), It will hit the session database in page life cycle.
You have to set below directive on Error.aspx Page to tell ASP.Net not to load session information for it:
EnableSessionState="false"
Please note this will only work if you are not using any session information in the error page.
Additionally, I also managed Global.asax page as below :
private static Exception startException;
protected void Application_Start()
{
try
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ModelBinders.Binders.Add(typeof(DateTime), new MyDateTimeModelBinder());
}
catch (Exception ex)
{
startException = ex;
}
}
static HashSet<string> allowedExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
".js", ".css", ".png",".jpg",".jpeg",".gif",".bmp"
};
public bool IsStaticFile(HttpRequest request)
{ //My project was a mix of asp.net web forms & mvc so had to write this
if (Request.RawUrl.ToLower().Contains("/bundles/") || Request.RawUrl.ToLower().Contains("/frontendcss?") ||
Request.RawUrl.ToLower().Contains("/fonts/")
//these are my css & js bundles. a bit of hack but works for me.
)
{
return true;
}
string fileOnDisk = request.PhysicalPath;
if (string.IsNullOrEmpty(fileOnDisk))
{
return false;
}
string extension = Path.GetExtension(fileOnDisk).ToLower();
return allowedExtensions.Contains(extension);
}
protected void Application_BeginRequest()
{
if (startException != null && Request.RawUrl.ToLower() == "/Error.aspx".ToLower())
{
return;
}
if (startException != null && IsStaticFile(Request))
{
return;
}
if (startException != null && Request.RawUrl.ToLower()!= "/Error.aspx".ToLower())
{
//Response.Redirect("Error.aspx");
Server.Transfer("Error.aspx");
this.Context.AddError(startException);
return;
}
}
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
Response.Clear();
Server.ClearError();
Server.Transfer("Error.aspx");
}