How can I execute the ActionMethod program with software?
I am trying to execute Controller Action Method programmatically and I am not sure how to do it.
Scenario: When my ControllerFactory can't find the controller, I want it to manually execute one action method I have on a simple, custom controller. I don't want to rely on using any route data to define the controller / method .. because that route may not have been connected.
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 MyCustomController();
((MyCustomController)result).Execute404NotFound(); // <-- HERE!
}
return result;
}
}
.. and this way ...
public static void Execute404NotFound(this Controller controller)
{
result = new 404NotFound();
// Setup any ViewData.Model stuff.
result.ExecuteResult(controller.ControllerContext); // <-- RUNTIME
// ERROR HERE
}
Now when I run the controller factory, the controller cannot be found, then I manually create my own base controller. Then I call the extension method ' Execute404NotFound
' on that controller instance. That's fine .. until it executes the method ExecuteResult(..)
. What for? the controller has no data ControllerContext
. So it crashes ExecuteResult
because it takes multiple ControllerContext
.
So - can someone out there help me? see what i am doing wrong.
Remember -> I am trying to get my controller factory to manually / programmatically call a method on the controller, which will of course return an ActionResult.
Please, help!
UPDATE:
I also try to avoid the more common solutions of using catchall or throwing an exception that is handled in the Application_Exception section. I don’t believe they should be able to properly handle this problem .. given that we know what the problem is and as such we should handle it right there.
a source to share
If you create a controller manually, you also need to provide a context for the controller.
You are on the right track, but you will need some more code to complete it. I went through this a while ago and a whole bunch of code popped up in this thread on how to instantiate and fake a request to the controller .
If all you're doing is looking for a 404 code, your approach seems overkill. Could you do something like this ?
a source to share
You can throw out new HttpException(404, "Not found")
of your custom controller instance which can be found in Application_Error
in global.asax and redirected to the page you want:
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 ) );
}
a source to share
The problem is that the factory manager's job is not to invoke action methods.
I can think of two ways to deal with this besides Application_Error:
-
override System.Web.Mvc.MvcHandler ProcessRequest method. This is a nuclear option in my opinion.
-
if no controller is found, return your 404 controller and there are a number of ways to make this the most straight forward, to override the ExecuteCore method of the controllers so that your action method is always called.
a source to share
How about changing 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;
}
}
a source to share