I'm new to ASP.NET and I have a very basic site that I just want to grab all Application Errors and email them to me, while giving the user a error page. I read a lot on the subject and seems to be a lot of information. Below is what I came up with but I'm having problems keeping the Exception in session so I can email it to me.
I keep getting a NullReferenceException was unhandled by user code on the ex.Message from the Error.aspx.cs file.
Any thoughts?
Global.asax.cs-
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
HttpContext.Current.Response.Redirect("~/Error.aspx");
}
Error.aspx.cs-
public partial class Error : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Exception ex = (Exception)Session["Exception"];
this.SendEmail(ex);
Session.Remove("Exception");
}
}
private void SendEmail(Exception ex)
{
string body = "An exception occured at "
+ DateTime.Now.ToLongTimeString()
+ " on " + DateTime.Now.ToLongDateString()
+ "<br />" + ex.Message; //ERROR HERE
MailMessage msg = new MailMessage("from#email.com", "to#email.com");
msg.Subject = "Exception in Portal Website";
msg.Body = body;
msg.IsBodyHtml = true;
SmtpClient client = new SmtpClient("localhost");
client.Send(msg);
}
}
Your problem is that you are never setting Session["Exception"], so on your Error.aspx, Exception ex is always going to be null... and then doing ex.Message will throw the NullReferenceException. Even if you fix that and appropriately set Session["Exception"], what you are doing isn't ideal.
You could just send the email from the Application_Error function.
Your Response.Redirect (MSDN Entry) is going to cause ANOTHER exception (ThreadAbortException)
You should look at implementing ELMAH. It is easy to implement and does everything you want to do.
But if all you want to do is fix your code, just move the send email logic to Application_Error and then you don't have to worry about Session.
What I did in a similar instance is to throw all exceptions and then in your global.asax uses the Application_Error method to perform any work you need to with the error message.
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
string exception;
Exception ex = Server.GetLastError();
if (ex.InnerException != null)
{
Exception innerException = ex.InnerException;
exception = ex.InnerException.Message;
}
else
{
exception = ex.Message;
}
//Send email here
//redirect to error page
}
I don't see you pushing the exception object to session, why not move the SendEmail method to global.ascx and SendEmail then redirect user to Error page. There are third party plugins like ELMAH that can do all this easily for you with simple configurations.
I'm in a similar bind. I'm going to check out ELMAH, but my project has a huge restriction on Open Source Projects, yours may to. Sucks to be me!
In which case I'd suggest creating a logging function for the message that you can call directly from the Global.asax. Something like:
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
LogException(ex);
HttpContext.Current.Response.Redirect("~/Error.aspx");
}
//Somewhere Else
void LogException(Exception ex)
{
WriteExceptionToEndOfTextFile(ex);
SendEmail();
}
This way if someone decides they want to suppress our exceptions, they can still properly handle the exception.
Related
Looking at:
https://learn.microsoft.com/en-us/aspnet/web-forms/overview/getting-started/getting-started-with-aspnet-45-web-forms/aspnet-error-handling
and specifically:
private void Page_Error(object sender, EventArgs e)
{
Exception exc = Server.GetLastError();
// Handle specific exception.
if (exc is HttpUnhandledException)
{
ErrorMsgTextBox.Text = "An error occurred on this page. Please verify your " +
"information to resolve the issue."
}
// Clear the error from the server.
Server.ClearError();
}
Is there a way to only handle asp.net file uploader file size too big (e.g. over 50MB) and let all other errors be handled at the application level?
BTW, here is code to catch files that are too big at the application level:
//Global.asax
private void Application_Error(object sender, EventArgs e)
{
var ex = Server.GetLastError();
var httpException = ex as HttpException ?? ex.InnerException as HttpException;
if(httpException == null) return;
if (((System.Web.HttpException)httpException.InnerException).WebEventCode == System.Web.Management.WebEventCodes.RuntimeErrorPostTooLarge)
{
//handle the error
Response.Write("Too big a file, dude"); //for example
}
}
So in other words, can we "throw" an application level error from a page level error method (e.g., when it's anything other than that file size exception that we want to handle on that specific page)?
I am using Application_Error method to handle and log exceptions. I need to pass Exception from Global.asax to Error controller
I tried
this.Session["w"] = (Exception)exception;
but that's not working.
My code looks like this:
protected void Application_Error(Object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
if (exception != null)
{
this.Session["w"] = (Exception)exception;
Server.ClearError();
ExceptionManager.LogExceptionToTextFile(exception);
ExceptionManager.LogExceptionToEmail(exception);
if (!Response.IsRequestBeingRedirected)
Response.Redirect("~/Error/Index");
}
}
You can try something like this
Exception exception = Server.GetLastError();
string errorId = Guid.NewGuid().ToString();
if (exception != null)
{
this.Application.Lock();
this.Application[errorId] = exception;
this.Application.Unlock();
Server.ClearError();
ExceptionManager.LogExceptionToTextFile(exception);
ExceptionManager.LogExceptionToEmail(exception);
if (!Response.IsRequestBeingRedirected)
Response.Redirect("~/Error/Index?errorId="+errorId);
}
and in the error controller
var Exception = (Exception)Application[Request["errorId"]]
EDIT:
If you want to use sessions, You might also want to replace
Response.Redirect("~/Error/Index");
With
Response.Redirect("~/Error/Index", false);
Reason you loose your session, is due to thread cancellation.
You can read more about it at When Should I Use Response.Redirect(url, true)?:
I have tried this code to raise a manual exception
protected void test ()
try
{
throw new Exception("HI"); //line22
}
catch (Exception ex) { lblerror.Text = ex.ToString(); }
but received exception below
System.ArgumentException: HI at
Project_Test_M_Test.btnsubmit_Click(Object sender, EventArgs e) in
D:\Project\Test\M_Test.aspx.cs:line 22
I want to see error message that I have send not this.
Please use ex.Message instead of ex.ToString().
btw, its not a good idea to throw the base class Exception. please use a more specific one.
This is what you need to do, use Message property to access the error message.
protected void test ()
{
try
{
throw new Exception("HI"); // Exception message passed from constructor
}
catch (Exception ex)
{
lblerror.Text = ex.Message;
}
}
Is there a better way to catch all exceptions in one place without writing try{} catch{} for each method?
You can catch the exceptions in your application by overwriting Application_Error found in Global.asax. However, using this approach you cannot act on these exceptions like you could using a regular try catch block.
You can log it
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
// if there's an Inner Exception we'll most likely be more interested in that
if (ex .InnerException != null)
{
ex = ex .InnerException;
}
// filter exception using ex.GetType() ...
// log exception ...
// Clear all errors for the current HTTP request.
HttpContext.Current.ClearError();
}
And/or redirect
void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
// process exception
// redirect
HttpContext.Current.ClearError();
Response.Redirect("~/Error.aspx", false);
return;
}
And this is about all your can do.
Thanks All. I got an answer from a site.Here is the code i have modified according to the exception and logging errors with a third party(Elmah) in the application. Hope, it will be helpful for others.
void Application_Error(object sender, EventArgs e)
{
// Code that runs when an unhandled error occurs
//Get the exception object
Exception exceptionObject = Server.GetLastError();
try
{
if (exceptionObject != null)
{
if (exceptionObject.InnerException != null)
{
exceptionObject = exceptionObject.InnerException;
}
switch (exceptionObject.GetType().ToString())
{
case "System.Threading.ThreadAbortException":
HttpContext.Current.Server.ClearError();
break;
default:
// log exception ...
//Custom method to log error
Elmah.ErrorSignal.FromCurrentContext().Raise(exceptionObject);
break;
}
}
}
catch
{
//Avoiding further exception from exception handling
}
}
For testing purposes, I have this method:
public ActionResult Index()
{
System.Diagnostics.Debug.Write("Index");
return new HttpNotFoundResult();
}
The method is being called ('Index' is outputted). Somehow, it is causing a HttpException to be thrown. In my Global.asax file I have an Application_Error implementation:
void Application_Error(object sender, EventArgs e)
{
Exception exc = Server.GetLastError();
if (exc is System.Web.HttpException)
{
string msg = "UrlReferrer: "
+ Context.Request.UrlReferrer + " UserAgent: " + Context.Request.UserAgent
+ " UserHostAddress: " + Context.Request.UserHostAddress + " UserHostName: "
+ Context.Request.UserHostName;
ErrorHandler.Error(exc.Message, msg);
}
else {
...
}
}
This method is being called after the system has processed the request for Index. I think that the HttpNotFoundResult causes the exception to be thrown - or perhaps the exception is thrown for any ActionResult with a status code of 404.
This is quite annoying, as it is side-stepping the OnException handler on my controller. For my website, Application_Error is supposed to be a last-ditch fallback - most normal errors are intended to be handled in other places (by the controllers, or action filters). I only want Application_Error to log completely unexpected exceptions, or 404s for things like image or .js files.
Is there a way to stop asp.net from throwing exceptions for programatically generated 404s? Alternatively, is there a way to determine in Application_Error if the HttpException was caused by a programatically generated 404?
You can create a custom exception filter that handles 404 exceptions raised by all the actions. You could use HttpContext.Items collection to track whether it is a programatically raised 404 or not.
Custom exception filter
public class NotFoundExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
// ignore if the exception is already handled or not a 404
if (filterContext.ExceptionHandled || new HttpException(null, filterContext.Exception).GetHttpCode() != 404)
return;
filterContext.HttpContext.Items.Add("Programmatic404", true);
filterContext.ExceptionHandled = true;
}
}
You need to apply NotFoundExceptionFilter as a global filter.
Application_Error event
public static void Application_Error(object sender, EventArgs e)
{
var httpContext = ((MvcApplication)sender).Context;
// ignore if it is a programatically raised 404
if(httpContext.Items["Programmatic404"] != null && bool.Parse(httpContext.Items["Programmatic404"].ToString()))
return;
// else, Log the exception
}