I have my custom error handler defined in Global.asax:
void Application_Error(Object sender, EventArgs e)
{
var exception = Server.GetLastError();
var httpException = exception as HttpException;
Response.Clear();
Server.ClearError();
var routeData = new RouteData();
routeData.Values["controller"] = "Error";
routeData.Values["action"] = "General";
routeData.Values["exception"] = exception;
Response.StatusCode = 500;
if(httpException != null)
{
Response.StatusCode = httpException.GetHttpCode();
switch(Response.StatusCode)
{
case 403:
routeData.Values["action"] = "Http403";
break;
case 404:
routeData.Values["action"] = "Http404";
break;
}
}
IController errorController = new ErrorController();
var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
errorController.Execute(rc);
}
I'm trying to test my error handler by raising an error like this in my service model:
if(albums != null)
{
albums = null;
ctx.SpotifyAlbums.AddRange(albums);
ctx.SaveChanges();
}
This raises an exception, but how can I "catch" this exception with my custom error handler?
I usually create a controller with actions that throw specific exceptions.
public class ErrorTestController : Controller
{
public ActionResult ThrowHttpException(int httpStatusCode)
{
throw new HttpException(httpStatusCode, "Error!");
}
public ActionResult ThrowApplicationError()
{
Throw new Exception("Boo!");
}
}
Not fancy, but it does the job. And a 404 handler is the easiest to test - just enter a bad url.
Related
Sorry if this is a duplicate question. However, I've tried looking for the answer and can't seem to find it.
Is there a way in ASP.NET to redirect to a page when a specific error occurs (in my case, when the request is too large). This needs to be just when the error occurs on a specific page, and not just on any page.
Thanks in advance!
As ADyson says in the comments, perhaps a try - catch block could be used for this situation.
try
{
// put the code that you want to try here
}
catch(Exception specificException)
{
return RedirectToAction(actionName, controllerName, routeValues);
}
Let me know if this helps.
Yes! there is as follows:
In the Global.asax file:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
HttpException httpException = exception as HttpException;
if (httpException != null)
{
if (httpException.GetHttpCode() == 404)
{
Server.ClearError();
Response.Redirect("~/Home/PageNotFound");
return;
}
}
//Ignore from here if don't want to store the error in database
HttpContextBase context = new HttpContextWrapper(HttpContext.Current);
RouteData routeData = RouteTable.Routes.GetRouteData(context);
string controllerName = null;
string actionName = null;
if (routeData != null)
{
controllerName = routeData.GetRequiredString("controller");
actionName = routeData.GetRequiredString("action");
}
ExceptionModel exceptionModel = new ExceptionModel()
{
ControllerName = controllerName ?? "Not in controller",
ActionOrMethodName = actionName ?? "Not in Action",
ExceptionMessage = exception.Message,
InnerExceptionMessage = exception.InnerException != null ? exception.InnerException.Message : "No Inner exception",
ExceptionTime = DateTime.Now
};
using (YourDbContext dbContext = new YourDbContext())
{
dbContext.Exceptions.Add(exceptionModel);
dbContext.SaveChanges();
}
// Ignore till here if you don't want to store the error on database
// clear error on server
Server.ClearError();
Response.Redirect("~/Home/Error");
}
Then in the controller:
public class HomeController : Controller
{
[AllowAnonymous]
public ActionResult Error()
{
return View();
}
[AllowAnonymous]
public ActionResult PageNotFound()
{
return View();
}
}
Here is everything you need to handle error in ASP.NET MVC Application.You can also customize according to your personal preference.
I am trying to understand custom exceptionhandlers but am not getting the hang of it. I tried implementing a custom exception handler like explained in the following pages:
https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/
https://learn.microsoft.com/en-us/aspnet/web-api/overview/error-handling/web-api-global-error-handling
Now my code is:
public class CustomRestErrorHandlerException : ExceptionHandler
{
public void CustomError(String error)
{
var message = new HttpResponseMessage(HttpStatusCode.NoContent)
{
Content = new StringContent("An unknown error occurred saying" + error)
};
throw new HttpResponseException(message);
}
public override void Handle(ExceptionHandlerContext context)
{
if (context.Exception is ArgumentNullException)
{
var result = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "ArgumentNullException"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else if (context.Exception is ArgumentException)
{
var result = new HttpResponseMessage(HttpStatusCode.NoContent)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Argument is not found"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else if (context.Exception is ArgumentOutOfRangeException)
{
var result = new HttpResponseMessage(HttpStatusCode.NotImplemented)
{
Content = new StringContent(context.Exception.Message),
ReasonPhrase = "Argument is out of range"
};
context.Result = new ErrorMessageResult(context.Request, result);
}
else
{
CustomError(context.Exception.Message);
}
}
public class ErrorMessageResult : IHttpActionResult
{
private HttpRequestMessage _request;
private HttpResponseMessage _httpResponseMessage;
public ErrorMessageResult(HttpRequestMessage request, HttpResponseMessage httpResponseMessage)
{
_request = request;
_httpResponseMessage = httpResponseMessage;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
return Task.FromResult(_httpResponseMessage);
}
}
}
Then I try to call the exceptionhandler which I obviously do wrong : (this I probably do not understand <<
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
try
{
// try clearing
}
catch(Exception e)
{
throw new CustomRestErrorHandlerException(); << error occurs here
}
return this.Ok();
}
As you can see the error occurs because the exception is not an exceptionhandler but I have no idea how to then throw an exception through an custom exception handler since it's explained nowhere.
Can anyone explain this to me with a small example perhaps?
ExceptionHandler's have to be registered in the Web API configuration. This can be done in the WebApiConfig.cs file as shown below, where config is of type System.Web.Http.HttpConfiguration.
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
Once they are registered they are automatically called during unhandled exceptions. To test it out you might want to throw an exception in the action method such as:
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
try
{
// try clearing
}
catch(Exception e)
{
throw new ArgumentNullException(); << error occurs here
}
return this.Ok();
}
You can now put a breakpoint in your exception handler and see how the unhandled exception is caught by the global ExceptionHandler.
Quote from: https://www.exceptionnotfound.net/the-asp-net-web-api-exception-handling-pipeline-a-guided-tour/:
"The handler, like the logger, must be registered in the Web API configuration. Note that we can only have one Exception Handler per application."
config.Services.Replace(typeof(IExceptionHandler), new CustomRestErrorHandlerException ());
So add the line above to the WebApiConfig.cs file and then simply throw an exception from the controller:
[Route("api/***/***")]
[HttpGet]
public IHttpActionResult Clear****()
{
// do not use try catch here
//try
//{
// try clearing
//}
//catch(Exception e)
//{
//throw new CustomRestErrorHandlerException(); << error occurs here
//}
throw new Exception();
}
I am coding a MVC 5 internet application and have a question in regards to the HttpRequestValidationException exception.
My previous code in my controller is as follows:
protected override void OnException(ExceptionContext filterContext)
{
// Make use of the exception later
this.Session["ErrorException"] = filterContext.Exception;
if (filterContext.Exception is HttpRequestValidationException)
{
TempData["UITitle"] = "Validation";
TempData["UIHeading"] = customErrorType;
TempData["UIMessage"] = filterContext.Exception.Message;
TempData["UIException"] = filterContext.Exception;
filterContext.ExceptionHandled = true;
}
else
{
TempData["UITitle"] = "Error";
TempData["UIHeading"] = customErrorType;
TempData["UIMessage"] = filterContext.Exception.Message;
TempData["UIException"] = filterContext.Exception;
}
filterContext.Result = this.RedirectToAction("Index", "Error");
base.OnException(filterContext);
}
If an exception occurred, then the Index view in the Error controller displayed this error.
I have now written the following global filter:
public class ExceptionFilterDisplayErrorView : IExceptionFilter
{
public virtual void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
RouteValueDictionary routeValueDictionary = new RouteValueDictionary();
routeValueDictionary.Add("controller", "Error");
routeValueDictionary.Add("action", "Index");
filterContext.Controller.TempData.Clear();
filterContext.Controller.TempData.Add("UITitle", "Error");
filterContext.Controller.TempData.Add("UIHeading", "Error");
filterContext.Controller.TempData.Add("UIMessage", filterContext.Exception.Message);
filterContext.Controller.TempData.Add("UIException", filterContext.Exception);
RedirectToRouteResult redirectToRouteResult = new RedirectToRouteResult(routeValueDictionary);
filterContext.Result = redirectToRouteResult;
}
}
The above filter works the same as the previous OnException function, except now, if a HttpRequestValidationException exception occurs, the default stack trace page is shown, rather than the Error controller view.
Is it possible to display a custom error view for HttpRequestValidationException exceptions in an exception filter?
Something like this works for me.
public class CustomExceptionAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled)
{
int val = (int)(((Exception)filterContext.Exception).ActualValue);
filterContext.Result = new ViewResult
{
ViewName = "CustomError",
ViewData = new ViewDataDictionary<int>(val)
};
filterContext.ExceptionHandled = true;
}
}
}
** EDIT ***
public class HttpRequestValidationExceptionAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled && filterContext.Exception is HttpRequestValidationException)
{
IDictionary val = filterContext.Exception.Data;
filterContext.Result = new ViewResult
{
ViewName = "RangeError",
ViewData = new ViewDataDictionary<IDictionary>(val)
};
filterContext.ExceptionHandled = true;
}
}
}
I am handling error in Base controller. I need to display the error stored in tempdata, Exception type in a razor view. How can I do that?
Base Controller code
protected override void OnException(ExceptionContext filterContext)
{
// if (filterContext.ExceptionHandled)
// return;
//Let the request know what went wrong
filterContext.Controller.TempData["Exception"] = filterContext.Exception.Message;
//redirect to error handler
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(
new { controller = "Error", action = "Index" }));
// Stop any other exception handlers from running
filterContext.ExceptionHandled = true;
// CLear out anything already in the response
filterContext.HttpContext.Response.Clear();
}
Razor View Code
<div>
This is the error Description
#Html.Raw(Html.Encode(TempData["Exception"]))
</div>
Try to make common exception attribute handling and register it as global filters. Like,
Common Exception Handling attribute :
/// <summary>
/// This action filter will handle the errors which has http response code 500.
/// As Ajax is not handling this error.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class HandleErrorAttribute : FilterAttribute, IExceptionFilter
{
private Type exceptionType = typeof(Exception);
private const string DefaultView = "Error";
private const string DefaultAjaxView = "_Error";
public Type ExceptionType
{
get
{
return this.exceptionType;
}
set
{
if (value == null)
{
throw new ArgumentNullException("value");
}
this.exceptionType = value;
}
}
public string View { get; set; }
public string Master { get; set; }
public void OnException(ExceptionContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (!filterContext.IsChildAction && (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled))
{
Exception innerException = filterContext.Exception;
// adding the internal server error (500 status http code)
if ((new HttpException(null, innerException).GetHttpCode() == 500) && this.ExceptionType.IsInstanceOfType(innerException))
{
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
// checking for Ajax request
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var result = new PartialViewResult
{
ViewName = string.IsNullOrEmpty(this.View) ? DefaultAjaxView : this.View,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData
};
filterContext.Result = result;
}
else
{
var result = this.CreateActionResult(filterContext, model);
filterContext.Result = result;
}
filterContext.ExceptionHandled = true;
}
}
}
private ActionResult CreateActionResult(ExceptionContext filterContext, HandleErrorInfo model)
{
var result = new ViewResult
{
ViewName = string.IsNullOrEmpty(this.View) ? DefaultView : this.View,
MasterName = this.Master,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
TempData = filterContext.Controller.TempData,
};
result.TempData["Exception"] = filterContext.Exception;
return result;
}
}
And Error/_Error view
#model HandleErrorInfo
<div>
This is the error Description
#TempData["Exception"]
</div>
I agree that you should never expose an exception to your view but if you really need to, try using a custom attribute.
public class CustomExceptionAttribute : System.Web.Mvc.HandleErrorAttribute
{
public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled)
{
filterContext.Controller.TempData.Add("Exception", filterContext.Exception);
filterContext.ExceptionHandled = true;
}
}
}
public class MyController : System.Web.Mvc.Controller
{
[CustomException]
public ActionResult Test()
{
throw new InvalidOperationException();
}
}
If you override the OnException method in the base controller, then every action will get an Exception object placed in temp data. This maybe the desired behavior but with an attribute you can selectively enable this feature.
I would strongly suggest not to show any detailed exception information in any public facing application as this could end up as a security issue. However, if this is an intranet application with controlled access or if you REALLY want to show the exception details, create a DisplayTemplate and use it as follows:
<div>
Exception Details
#Html.Display(TempData["Exception"])
</div>
I'm trying to pass the accessed url to my error controller called ErrorController so that I can log what page was being accessed at the time.
In my Global.asax.cs I have a method Application_Error looking like this:
protected void Application_Error(object sender, EventArgs e)
{
var httpContext = ((MvcApplication)sender).Context;
var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
var currentController = " ";
var currentAction = " ";
if (currentRouteData != null)
{
if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
{
currentController = currentRouteData.Values["controller"].ToString();
}
if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
{
currentAction = currentRouteData.Values["action"].ToString();
}
}
var ex = Server.GetLastError();
var controller = new ErrorController();
var routeData = new RouteData();
var action = "Index";
if (ex is HttpException)
{
var httpEx = ex as HttpException;
switch (httpEx.GetHttpCode())
{
case 404:
action = "NotFound";
// Pass along some data about accessed page here
break;
// others if any
default:
action = "Index";
break;
}
}
httpContext.ClearError();
httpContext.Response.Clear();
httpContext.Response.StatusCode = ex is HttpException ? ((HttpException)ex).GetHttpCode() : 500;
httpContext.Response.TrySkipIisCustomErrors = true;
routeData.Values["controller"] = "Error";
routeData.Values["action"] = action;
controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
((IController)controller).Execute(new RequestContext(new HttpContextWrapper(httpContext), routeData));
}
And my ErrorController looks like this:
public class ErrorController : BaseController
{
private readonly ILog _logger;
public ErrorController()
{
_logger = LogManager.GetLogger("CustomHandleErrorAttribute.class");
}
//
// GET: /Error/
public ActionResult Index()
{
return View();
}
public ActionResult NotFound(string error)
{
_logger.Error(error);
return View();
}
}
How should I go about populating the error parameter so I can log this to my file?
I think you're over complicating it a bit. Just create a shared function that logs the exception and page in your error controller class. This way you can forget about routing all together. You can use the UrlReferrer property on the request to get the page that the error occurred on.
Global.asax (vb.net):
Sub Application_Error()
Dim ex As Exception = Server.GetLastError()
Dim page As String = If(Not IsNothing(Request), Request.UrlReferrer.AbsoluteUri, Nothing)
ErrorController.LogError(ex, page)
Server.ClearError()
End Sub
Ended up doing the following in my ErrorController.cs:
if (Request.Url != null)
{
var path = Request.Url.AbsoluteUri;
_logger.Error("404: " + path);
}