简体   繁体   中英

Catching action method not found on controller

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM