I have a C# MVC 3 controller with the HandleError attribute at the class level
[HandleError(View = "MyErrorPage")]
public class MyController : Controller
{
At the method level I've got:
[HttpPost]
[MyExceptionHandler]
public ActionResult DoSomeStuff(FormCollection fc)
{
where MyExceptionHandler looks like:
public class MyExceptionHandlerAttribute : FilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
//do Stuff
JsonResult result = new JsonResult {
Data = new {
error = "Your request blah blah...."
}
};
filterContext.Result = result;
filterContext.ExceptionHandled = true;
}
}
This works perfectly fine on my localhost. I.e. upon an exception in method 'DoSomeStuff' [MyExceptionHandler] is invoked, the class level [HandleError] is not.
However, on a dev environment, the class level [HandleError] comes into play. The result is that the HandleError view=MyErrorPage is rendered.
I'm aware that [HandleError] is redundant on localhost but open to correction (or configuration options)?
I'm unsure if on the development environment (not localhost) MyExceptionHandler is invoked at all. It may be invoked before the class level [HandleError]?
My problem is that I need [MyExceptionHandler] to be the only handler invoked.
I'd like to be able to have [HandleError] invoked on my localhost so I can simulate the issue locally.
I've found the solution:
[HandleError(View = "MyErrorPage", Order = 2)]
solved the problem (Specifically, Order = 2).
The reason is that this handler now has a lower priority than the method level handler. Thus, the method level handler is executed first, as per the code above , it flags the exception as handled therefore the Controller level handler is never executed.
The reason the problem is only on localhost is because [HandleError] is not enabled on localhost by default. However, on the development environment it was automatically enabled.
I would then think that I'd be able to see the problem on localhost by setting:
<system.web>
<customErrors mode="On" />
but for some reason, that didn't work.... anyway,separate issue I guess.
FYI, I've attempted to summarise all this error handling stuff (with a focus on AJAX) here: http://outbottle.com/net-mvc-3-custom-ajax-error-handling-2/
Thanks
Related
we have an MVC application , what is the best way to record errors generated (by users) back to our support database? users should only see "this error has been reported" error message
all the details should be written to to a database
please help!
I suggest you either make use of External library like Log4Net or create you own library class which will do logging for you.
One more suggestion is make use of ExceptionFilter which is avaiable in MVC file and write code of logging in that filter. I am suggesting becuase it will not cause you to write code again and again it follows DRY principle.
Example :
public class CustomExceptionFilter: FilterAttribute,
IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
if (!filterContext.ExceptionHandled)
{
//log error here
Logger.LogException(filterContext.Exception);
//this will redirect to error page and show use logging is done
filterContext.Result = new
RedirectResult("customErrorPage.html");
filterContext.ExceptionHandled = true;
}
}
}
than you can do like this
//Over controller
[CustomExceptionFilter]
public class HomeController:Controller
{
//......
}
//Over the Action
[CustomExceptionFilter]
public ActionResult Index()
{
//.......
}
Check this for log4net : log4net Tutorial
There seems to be some weird behavior in ASP.NET Web API (4.0.30506) that I haven't witnessed before.
What I'm seeing is that the same action filter attribute instance is reused over Web API requests. This is especially a problem in case this attribute gets dependencies injected to it, since those dependencies might be specific to the web request. I'm aware that it's better for attributes to be passive, but my assumption was that action filter attributes where not cached.
I searched for any articles, blog posts or Microsoft change logs that described this and the reason behind this, but I couldn't find a single thing. That makes me wonder whether there is something wrong with my configuration that makes this happening. Thing is however, that I'm able to reproduce this issue in a new and empty Visual Studio 2012 Web API project.
What I did was create a new empty project using the Visual Studio 2012 ASP.NET MVC 4 Web Application project with the "Web API" template. It comes with the Web API 4.0.20710.0 NuGet package. After that I added the following attribute:
[DebuggerDisplay("{id}")]
public class TestFilterAttribute : System.Web.Http.Filters.ActionFilterAttribute {
private static readonly List<int> used = new List<int>();
private static int counter;
private readonly int id;
public TestFilterAttribute() {
this.id = Interlocked.Increment(ref counter);
}
public override void OnActionExecuting(HttpActionContext actionContext) {
// Just for testing: I would expect this line never to throw, but it does.
if (used.Contains(this.id)) throw new Exception("WAT?");
used.Add(this.id);
base.OnActionExecuting(actionContext);
}
}
And I add this attribute to the ValuesController (part of the default template):
public class ValuesController : ApiController {
// GET api/values
[TestFilterAttribute]
public IEnumerable<string> Get() {
return new string[] { "value1", "value2" };
}
// ...
}
Now when I start the project, go to the /api/values in the browser and refresh that page a few times, the "WAT?" exception is thrown.
Is this normal behavior of Web API and if so, what's the reasoning about this? Or Did I miss some memo about this change somewhere? Does this make Web API attributes extra unsuited for doing dependency injection? Or am I'm doing something wrong?
Web API is built on top of MVC, thus it uses a lot of it's functionality.
Attribute instance re-usability is part of the aggressive caching introduced by MVC 3. This means that the same Attribute instance will most likely be used with all the Actions it is applied on. MVC pipeline will do it's best at trying to treat your Attribute class like a Singleton.
Because the same Attribute instance is reused, it's Constructor is not called and id is not incremented. If, for example, you increment id inside OnActionExecuting, all will work well.
You can still do everything you want with your Attribute. You only need to keep in mind that you are not guaranteed to always get a new instance created. The constructor shouldn't contain anything but initial initialization.
public TestFilterAttribute() {
// Instance will be reused thus this will not be called for each Action
}
public override void OnActionExecuting(HttpActionContext actionContext) {
// Called on each Action
}
I'm stumped as to why Server.TransferRequest doesn't seem to be working in MVC when you are outside a controller action method. To showcase this issue, I've made a new, completely empty ASP.NET MVC 4 project, with one 'home' controller that has 2 actions: 'Wrong', and 'Right'. What I want to do is detect when someone is going to 'Wrong' and issue a Server.TransferRequest to 'Right', and I need to do it inside the 'Application_BeginRequest' event. This code causes a 404:
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Request.Path.ToLower().Contains("wrong"))
{
// this causes a 404 (note the application is hosted as a
// virtual app under 'cool')
Server.TransferRequest("/cool/home/right", true);
}
}
But when I do it inside the 'Wrong' controller action method, it works fine:
public class HomeController : Controller
{
public ActionResult Wrong()
{
// this works
Server.TransferRequest("/cool/home/right");
return View();
}
public ActionResult Right()
{
return View();
}
}
In debugging this issue, I turned on failed request tracing, and found that the second time through the pipeline in the unsuccessful case, I see this:
And I'm wondering why it is switching to a 'StaticFile' handler. After seeing this, I turned on the setting runAllManagedModulesForAllRequests='true', which makes it work! Unfortunately, I can't use this setting in my project. Another thing I've found is that it will work if you are trying to Server.TransferRequest to an .aspx file.
Anybody got ideas?
I have an existing site which has a base controller class for all of its controllers which overrides the implementation of OnAuthorization. In the simplest cut down form:
protected override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
//Do Stuff
}
This all works fine and well and does what it wants at the right time. I now want to add a new global authorization that will run before all other authorisation attributes. For test purposes this attribute looks like this:
public class TestFilterAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
var text = "debug point";
return;
}
}
And is added to the Global Filters like this:
filters.Add(new TestFilterAttribute());
My problem is that the OnAuthorization of the controller always seems to run before my filter's one. Is there any way that I can change this? I've tried playing with the order property that you can set when adding it to the global filter collection but that doesn't seem to help.
I could probably move the logic of the Controller's OnAuthorization into a new filter attribute when order would probably be usable but I'd rather avoid major code restructuring if there is an easier way to do it.
I've been searching for information on the Controller.OnAuthorization method but the best I have found is http://msdn.microsoft.com/en-us/library/gg416513(v=vs.98).aspx which only talks about filters. I had assumed that they would work the same way on Controllers but they seem to be getting treated specially, in particular not respecting the order (not even int.MinValue gets in first so its not just that the controller has a very low order by default).
So any suggestions on how to get an auth filter to run as the very first thing?
The final solution used was to refactor the code. I took all of the methods that can be run on a filter attribute (eg OnAuthorization, OnActionExecuting, etc.) and moved them onto attributes named for the classes they came from. So the OnAuthorization from BasicController became the same method on BasicControllerAttribute.
These attributes were then applied to the controllers and the attributes are then inherited by all of the classes that subclass from BasicController which essentially maintains the same functionality.
However the attribute can have its Order set that allows you to play around with the running order however you want.
My takeaway from this was to never override those methods on the controller and to always use attributes. :)
I had a related problem with the Controller.OnAuthorization and the AuthorizationFilter.OnAuthorization methods order of execution.
The short answer is: you can't override that Controller.OnAuthorization
runs prior to other filters.
Detailed answer on the question 'why is it so?' is in the code from
ASP.NET MVC sources below.
There is also nice blog post on filter ordering.
Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
FilterProviders.cs
namespace System.Web.Mvc
{
public static class FilterProviders
{
static FilterProviders()
{
Providers = new FilterProviderCollection();
Providers.Add(GlobalFilters.Filters);
Providers.Add(new FilterAttributeFilterProvider());
Providers.Add(new ControllerInstanceFilterProvider());
}
public static FilterProviderCollection Providers { get; private set; }
}
}
ControllerInstanceFilterProvider.cs
using System.Collections.Generic;
namespace System.Web.Mvc
{
public class ControllerInstanceFilterProvider : IFilterProvider
{
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
if (controllerContext.Controller != null)
{
// Use FilterScope.First and Order of Int32.MinValue to ensure controller instance methods always run first
yield return new Filter(controllerContext.Controller, FilterScope.First, Int32.MinValue);
}
}
}
}
I have a base controller class where I'm overriding to the Controller.OnException handler method in order to provide a generic error handling for certain types of controllers that will inherit from this class (these are API controllers that will return JSON results). The OnException method never gets called when an exception gets raised by the controller. Does anyone see what I am doing wrong, or is there a better way to handle this situation?
Using MVC 1.0
Base Class:
public class APIController : Controller
{
protected override void OnException(ExceptionContext filterContext)
{
//This is never called
filterContext.Result =
new JsonResult();
base.OnException(filterContext);
}
}
Inheriting Class:
public class MyController : APIController
{
public AjaxResult ForcedException()
{
throw new SystemException();
}
}
If I understand your question correctly - your Exception must be marked as "handled" in your OnException. Try this:
filterContext.ExceptionHandled = true;
The MSDN documentation (http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.onexception.aspx) states that the OnException method is "called when an unhandled exception occurs in the action."
So, it sounds like it might only fire when an unhandled exception happens in an action method.
I created an new controller with one Index action. And the OnException method does in fact execute. I also tried throwing a SystemException() and the OnException method fired as well.
public ActionResult Index ( )
{
throw new NotImplementedException ( );
}
I was having exactly the same problem in my ASP.NET MVC 3 project.
It turns out that this is a feature of Visual Studio and the development server. When in debug mode you will be returned to Visual Studio and given the default exception dialog box.
One way to stop this from happening is to change the compilation mode in your Web.config from this:
<compilation debug="true" targetFramework="4.0">
To this:
<compilation debug="false" targetFramework="4.0">
You can also leave debug mode set to true but test your application is working by choosing the Start without debugging option from the visual studio menu (invoked by pressing Ctrl+F5.
Are you calling the controller action from a unit test, or via ASP.NET? If you're calling the method directly in a test, say through NUnit, then OnException won't fire: the test framework will handle the exception and turn it into a failed test.
The same rules apply for Controller.OnException as for HandleErrorAttribute
The required config for HandleErrorAttribute is noted here https://stackoverflow.com/a/528550/939634
If you have customError in your web.config set to mode="RemoteOnly" and you are debugging locally or you have mode="Off" the exceptions will not be handled and you will see the ASP.Net yellow screen with a stack trace.
If you set mode="On" or you are viewing the site remotely and have mode="RemoteOnly" your OnException method will execute properly.
Apply the [HandleError] attribute to the class.