MVC HttpRequestValidationException exception - c#

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;
}
}
}

Related

How to get the http status code in custom HandleErrorAttribute

I am using a custom HandleErrorAttribute which is applied globally by being registered in the filters. The problem is, I can't catch the proper http error code. Every time it is 200 code inside that attribute.
public class HandleAndLogErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
return;
var statusCode = filterContext.HttpContext.Response.StatusCode; //always 200
LogManager.Error(filterContext.Exception);
filterContext.Result = CreateActionResult(filterContext, statusCode);
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = statusCode;
//filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
protected virtual ActionResult CreateActionResult(ExceptionContext filterContext, int statusCode)
{
var ctx = new ControllerContext(filterContext.RequestContext, filterContext.Controller);
var statusCodeName = ((HttpStatusCode)statusCode).ToString();
var viewName = "~/Views/Shared/Error.cshtml";
var controllerName = (string)filterContext.RouteData.Values["controller"];
var actionName = (string)filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
var result = new ViewResult
{
ViewName = viewName,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
};
result.ViewBag.StatusCode = statusCode;
return result;
}
}
Going through the source code for HandleErrorAttribute.cs, it looks like you can get the status code using the below code
var statusCode = new HttpException(null, filterContext.Exception).GetHttpCode();

asp.net mvc validate [HttpPost] ActionResult()

I need to implement a ActionFilterAttribute [POST] ActionResult() in the controller. The problem is that I try to “redirect” to a page if validation failed... But it does not work. Validation runs, but then returns to the ActionResult() next line and finally when the view is returned, only then “redirected” to the page listed in the validation. Ultimately what I need is to stop the ActionResult() statements and “redirect” to the page listed in the validation. I tried OnActionExecuting() and OnActionExecuted() but does not work any
I need to...
filterContext.HttpContext.Response.Redirect (loginUrl, true);
Run away, “redirecting” the page indicated
My code:
[HelperSomeValidations("")]
[HttpPost]
public ActionResult Create(Pais pais)
{
try
{
PaisBLL.saveNew(pais);
}
catch (Exception ex)
{
ViewBag.error = ex;
return View(“Error”);
}
return RedirectToAction(“Index”);
}
public class HelperSomeValidations : ActionFilterAttribute
{
public HelperSomeValidations(String permiso)
{
this.permiso = permiso;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var user = filterContext.HttpContext.Session["coco"];
if (user == null) //validates if the user just login
{
//send them off to the login page
var url = new UrlHelper(filterContext.RequestContext);
var loginUrl = url.Content(“~/Usuario/Login”);
filterContext.HttpContext.Response.Redirect(loginUrl, true);
}
else
{
if (permission != “”)
{
//does some validations with “permission”
}
}
}
}
Thks!
I know this doesn't solve the problem you have posted but I feel it's a better solution. I would personally use an AuthoriseAttribute here instead as this is what it's designed todo.
public class Authorise : AuthorizeAttribute
{
private readonly string _permissionSystemName;
public Authorise()
{
}
public Authorise(string permissionSystemName)
{
_permissionSystemName = permissionSystemName;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//DO some logic and return True or False based on whether they are allowed or not.
return false;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary(
new
{
area = filterContext.HttpContext.Request.RequestContext.RouteData.Values["area"],
controller = "Generic",
action = "PermissionDenied"
})
);
}
}
Usage would be along the lines of:
[Authorise("SomePermissionName")]
public class MyController : Controller
{
}
Instead of calling filterContext.HttpContext.Response.Redirect(loginUrl, true), you need to set the filterContext.Result to a RedirectResult.

Asp.Net Mvc Display exception in View from tempdata

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>

global HandleErrorAttribute and custom Action HandleErrorAttribute be invoked many times in MVC4

I Register a GlobalFilters for HandleErrorAttribute:
public class AppHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
Exception ex = filterContext.Exception;
//TODO
//LogManager.GetLogger("Exception").Error(ex.Message);
if (filterContext.Exception is UserException){
if(!string.isNullOrEmpty(this.View))
{
filterContext.ExceptionHandled = true;
filterContext.Result = ...;//<===this.View(custom Page)
}
else{
filterContext.ExceptionHandled = true;
filterContext.Result = ...;//<==='XYZ' page(another custom page)
}
}
}
}
And In Web.Config Set:
<customErrors mode="On"/>
Edit Begin
And In FilterConfig I set:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
//filters.Add(new HandleErrorAttribute());
filters.Add(new AppHandleErrorAttribute() );
}
End
Then I just want the Action of 'Test()' to run AppHandleErrorAttribute for Once.
public class XXXController:Controller{
public ActionResult Test()
{
throw new UserException("test0x11", "test", null);
return View();
}
[AppHandleError(View="Index")]//<=======here I want the Test2 to Index View, but it will be call AppHandleError twice this time
//it always Redirect to 'XYZ' page
public string Test2()
{
throw new UserException("test0x12", "test", null);
return "haha";
}
public string Index(){...}
}
How can I do not call globle HandleError?

ASP.net MVC - Custom HandleError Filter - Specify View based on Exception Type

I am inheriting the HandleErrorAttribute in my MVC application so I can log the error:
public class HandleAndLogErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
base.OnException(filterContext);
if( filterContext.Exception != null )
{
// log here
}
}
}
I'm adding this as a global filter:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleAndLogErrorAttribute());
}
Is it possible to specify a custom view for specific exception types as well? For example:
if( filterContext.Exception is DivideByZeroException )
{
// how do i specify that the view should be DivideByZero?
}
Create a new filter which inherits HandleErrorAttribute (or implements IExceptionFilter directly)
Register it in global.asax (by replacing filters.Add(new HandleError());):
Here is a filter that I've created that tries to find a view per specific HTTP status code:
public class MyErrorHandler : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
return;
var statusCode = (int) HttpStatusCode.InternalServerError;
if (filterContext.Exception is HttpException)
{
statusCode = filterContext.Exception.As<HttpException>().GetHttpCode();
}
else if (filterContext.Exception is UnauthorizedAccessException)
{
//to prevent login prompt in IIS
// which will appear when returning 401.
statusCode = (int)HttpStatusCode.Forbidden;
}
_logger.Error("Uncaught exception", filterContext.Exception);
var result = CreateActionResult(filterContext, statusCode);
filterContext.Result = result;
// Prepare the response code.
filterContext.ExceptionHandled = true;
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusCode = statusCode;
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
}
protected virtual ActionResult CreateActionResult(ExceptionContext filterContext, int statusCode)
{
var ctx = new ControllerContext(filterContext.RequestContext, filterContext.Controller);
var statusCodeName = ((HttpStatusCode) statusCode).ToString();
var viewName = SelectFirstView(ctx,
"~/Views/Error/{0}.cshtml".FormatWith(statusCodeName),
"~/Views/Error/General.cshtml",
statusCodeName,
"Error");
var controllerName = (string) filterContext.RouteData.Values["controller"];
var actionName = (string) filterContext.RouteData.Values["action"];
var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
var result = new ViewResult
{
ViewName = viewName,
ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
};
result.ViewBag.StatusCode = statusCode;
return result;
}
protected string SelectFirstView(ControllerContext ctx, params string[] viewNames)
{
return viewNames.First(view => ViewExists(ctx, view));
}
protected bool ViewExists(ControllerContext ctx, string name)
{
var result = ViewEngines.Engines.FindView(ctx, name, null);
return result.View != null;
}
}

Categories