Okay, so I am trying to get my controller to go to the Error.cshtml under the Shared folder on error. I've got the filter configured at startup:
Global.asax
protected void Application_Start()
{
...
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
...
}
FilterConfig.cs
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
HomeController.cs
[HandleError(View = "Error")] <---- I have the HandleError attribute
public class HomeController : Controller
{
IDbConnection _connection = new SqlConnection(ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString);
[Authorize]
public ActionResult Index()
{
// get the users current events
try
{
ViewBag.UserEvents = _connection.Query<MyEvents>("select ...)", new { });
}
catch (Exception ex)
{
throw new HttpException(500, ex.Message);
}
return View();
}
...
}
And so when the Index method is throwing an exception because I didn't open the connection, it just gives me the default ASP.NET exception page. What did I miss here?
Thanks!
Are you by any chance running this on your local machine? HandleError by default doesn't show errors on the local machine if you have customErrors set to Off or RemoteOnly. Set it to On.
Related
Hello Stackoverflow people,in my mvc project i have static class where i load static data then using it in controllers.
public class StaticData
{
public static List<ITEM_TYPES> _itemTypes ;
public static void LoadData()
{
try
{
using (pfservicereference.Service1Client ctx = new Service1Client())
{
_itemTypes = ctx.GetItemTypes();
}
}
catch (Exception ex)
{
throw new HttpException(500,ex.Message);
}
}
}
But how to redirect to Custom Error Page If i have HttpException Here?
I Have set customErrors mode="On" But it didnt helps.Is there any way to Redirect?
You can redirect to custom error page using following approaches,
Approach 1:
You can use try catch block inside action method and redirect to custom error page.
public ActionResult Index()
{
try
{
//Code logic here
}
catch (HttpException ex)
{
if (ex.ErrorCode == 500)
return RedirectToAction("error", "error");
}
return View();
}
Approach 2:
You can use exception filter for catching errors in application level, based on the error code we can redirect to custom error pages.
For this approach you can create separate exception filter class and mapped application level in global.asax or controller level.
protected override void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception is HttpException)
{
HttpException exception = filterContext.Exception as HttpException;
if (exception.ErrorCode == 600)
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
{
{ "action", "Error" },
{ "controller", "Error" }
});
filterContext.ExceptionHandled = true;
}
}
I am going to create own exceptionfilter which was inherit from FilterAttribute and IExceptionFilter
Source code is given below :
public class IndexException : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext exceptionContext)
{
if (!exceptionContext.ExceptionHandled && exceptionContext.Exception is IndexOutOfRangeException)
{
exceptionContext.Result = new RedirectResult("/Content/ExceptionFound.html");
exceptionContext.ExceptionHandled = true;
}
}
}
But when my code get to Index method where exception generated manually, my filter can't work
[IndexException]
public ActionResult Index()
{
throw new Exception("Не может быть меньше нуля");
You have to register your filter IndexException in the ASP.NET MVC Pipeline via RegisterGlobalFilters in FilterConfig.cs.
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
// add your filter
filters.Add(new IndexException());
}
}
Your exception filter will only redirect to the ExceptionFound.html if a IndexOutOfRangeException is caught.In the example you provided you are throwing a generic Exception.
Either change your filter to catch all types of exceptions or change this line:
throw new Exception("Не может быть меньше нуля");
To this:
throw new IndexOutOfRangeException("Не может быть меньше нуля");
In my MVC application I am trying to handle errors in my Application_Error method of my HttpApplication. In that handler I do this:
Exception exc = Server.GetLastError();
I'm using Ninject which provides its own DefaultControllerFactory which will throw an exception for non-existent controllers which I can easily catch like this:
if (exc is MyApp.Web.App_Start.ControllerNotFoundException)
{
Response.Clear();
Response.StatusCode = (int)System.Net.HttpStatusCode.NotFound;
Server.ClearError();
log = false;
}
Which works great. I don't want to log these.
The problem is when the controller does exist, but the action does not. For example, I have somebody hitting: admin/config.php. I actually have an AdminController so that doesn't cause a ControllerNotFoundException, it gives me a HttpException with the text:
"A public action method 'config.php' was not found on controller 'MyApp.Web.Controllers.AdminController'."
But I'm other than parsing the text to detect that it's this type of HttpException and not some other, is there a way to tell this is an action not found rather than something else?
I believe this will do what you want. You can inherit the default AsyncControllerActionInvoker class and then inject it.
public class DependencyResolverForControllerActionInvoker : IDependencyResolver
{
private readonly IDependencyResolver innerDependencyResolver;
public DependencyResolverForControllerActionInvoker(IDependencyResolver innerDependencyResolver)
{
if (innerDependencyResolver == null)
throw new ArgumentNullException("innerDependencyResolver");
this.innerDependencyResolver = innerDependencyResolver;
}
public object GetService(Type serviceType)
{
if (typeof(IAsyncActionInvoker).Equals(serviceType) || typeof(IActionInvoker).Equals(serviceType))
{
return new MyAsyncControllerActionInvoker();
}
return this.innerDependencyResolver.GetService(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this.innerDependencyResolver.GetServices(serviceType);
}
}
public class MyAsyncControllerActionInvoker : AsyncControllerActionInvoker
{
public override bool InvokeAction(ControllerContext controllerContext, string actionName)
{
try
{
return base.InvokeAction(controllerContext, actionName);
}
catch (HttpException ex)
{
// Handle unknown action error
}
}
public override bool EndInvokeAction(IAsyncResult asyncResult)
{
try
{
return base.EndInvokeAction(asyncResult);
}
catch (HttpException ex)
{
// Handle unknown action error
}
}
}
Here is a link to the InvokeAction and EndInvokeAction methods so you can try to determine how best to handle any errors it throws.
Usage
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// Decorate the current dependency resolver
// (make sure to do this last if using a DI container -
// or alternatively register your type with the DI container)
DependencyResolver.SetResolver(
new DependencyResolverForControllerActionInvoker(DependencyResolver.Current));
}
}
Alternative
You could create a base controller and override the HandleUnknownAction method for a similar (but more tightly coupled) result.
I have this custom exception filter:
public class CustomExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
try
{
if (filterContext == null) return;
// Log error details to the DB
}
catch
{
// Intentional empty catch
}
}
}
Which is applied globally in RegisterGlobalFilters (which is called from Application_Start):
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomExceptionFilter());
filters.Add(new HandleErrorAttribute());
}
I then have a protected constructor on my BaseController which calls this method:
public Site GetSiteByUrl(string host)
{
var urls = _repo.Urls.Where(x => x.Host == host);
if (urls == null || !urls.Any())
throw new MultiTenancyException(String.Format("No URL record exists for request. Host = \"{0}\"", host));
if (urls.Count() > 1)
throw new MultiTenancyException(String.Format("Multiple URL records exist for request. Host = \"{0}\"",
host));
var url = urls.Single();
var site = _repo.Sites.Single(x => x.Id == url.SiteId);
if (!url.Enabled)
throw new MultiTenancyException(
String.Format("URL record found for request, but is not Enabled. Host = \"{0}\", Site = \"{1}\"",
host, site.Name));
return site;
}
When any of the MultiTenancyExceptions in this method are thrown the OnException event of my CustomExceptionFilter is not triggered.
I've tried:
Using a basic Exception rather than my custom MultiTenancyException.
Applying the CustomExceptionFilter as an attribute on the controller rather than globally via RegisterGlobalFilters.
Both to no avail. Looking back through the logs of exceptions that have been caught by the CustomExceptionFilter the only logged errors seem to by system exceptions (NullReference, ArgumentOutOfRange etc.) which may be relevant or may simply be a coincidence.
I've been Googling around for about 30 minutes and am starting to bang my head against the wall at this point, so I'm looking for any sensible ideas.
Thanks.
Filters from GlobalFilterCollection are applied only for exceptions occurred while controller action execution.
To catch exceptions occurred before or later action execution you should define Application_Error method in your Global.asax file:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
void Application_Error(object sender, EventArgs e)
{
// handle global errors here
}
}
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?