开发者

How can i Execute a Controller's ActionMethod programatically?

I'm trying to execute a controller's Action Method programatically and I'm not sure how.

Scenario: When my ControllerFactory fails to find the controller, I wish it to manually execute a single action method which i have on a simple, custom controller. I don't want to rely on using any route data to determine the controller/method .. because that route might not have been wired up.

Eg.

// NOTE: Error handling removed from this example.
public class MyControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        IController result = null;
   
        // Try and load the controller, based on the controllerType argument.
        // ... snip
        // Did we retrieve a controller?
        if (controller == null)
        {
            result = new MyCu开发者_C百科stomController();
            ((MyCustomController)result).Execute404NotFound(); // <-- HERE!
        }

        return result;
    }
}

.. and that method is ..

public static void Execute404NotFound(this Controller controller)
{
    result = new 404NotFound();
    // Setup any ViewData.Model stuff.
    result.ExecuteResult(controller.ControllerContext); // <-- RUNTIME 
                                                        //  ERROR's HERE
}

Now, when I run the controller factory fails to find a controller, i then manually create my own basic controller. I then call the extension method 'Execute404NotFound' on this controller instance. That's fine .. until it runs the ExecuteResult(..) method. Why? the controller has no ControllerContext data. As such, the ExecuteResult crashes because it requires some ControllerContext.

So - can someone out there help me? see what I'm doing wrong.

Remember -> i'm trying to get my controller factory to manually / programmatically call a method on a controller which of course would return an ActionResult.

Please help!

UPDATE:

I'm also trying to avoid the more common solutions of using a catchall or throwing an exception which is handled in the Application_Exception section. I don't believe they should be proper way to handle this issue .. considering we know what the problem is and as such we should be handling it, there and then.


If you're creating a controller manually, you have to supply a context for the controller manually as well.

You're on the right track, but you're going to need some more code to finish it off. I went through this myself a while back, and there's a pretty thorough bunch of code over on this thread on how to instantiate and fake a request to a controller.

If all you're doing is looking to run some code on a 404, your approach seems like overkill. Couldn't you do something like this?


You could throw a new HttpException(404, "Not found") from your custom controller instance which could be caught in Application_Error in global.asax and redirect to the proper page:

protected void Application_Error(object sender, EventArgs e)
{
    var exception = Server.GetLastError();
    Response.Clear();
    var httpException = exception as HttpException;
    var routeData = new RouteData();
    routeData.Values.Add("controller", "Error");

    if(httpException != null )
    {
        switch( httpException.GetHttpCode() )
        {
            case 404:
                // Page not found.
                routeData.Values.Add("action", "HttpError404" );
                break;
            case 500:
                // Server error.
                routeData.Values.Add("action", "HttpError500" );
                break;
            default:
                routeData.Values.Add("action", "General" );
                break;
        }
    }

    routeData.Values.Add("error", exception);
    Server.ClearError();
    var errorController = new ErrorController();
    errorController.Execute(new RequestContext(
        new HttpContextWrapper(Context), routeData ) );
}


The problem here is that it is not the controller factories job to call action methods.

I can think of two ways of dealing with this besides Application_Error:

  1. override System.Web.Mvc.MvcHandler's ProcessRequest method. This is a nuclear option in my opinion.

  2. if the controller is not found return your 404 controller and, theres a number of ways to do this the most straight forward being to, override the controllers ExecuteCore method so that your action method is always the one called.


What about a nice simple

RedirectToAction("ActionName");

Hope this helps?


What about modifying the route:

public class MyControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        IController result = null;

        // Try and load the controller, based on the controllerType argument.
        // ... snip
        // Did we retrieve a controller?
        if (controller == null)
        {
            result = new MyCustomController();
            requestContext.RouteData.Values["action"] = "My404Action";
        }

        return result;
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜