Reputation: 86937
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 MyCustomController();
((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!
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.
Upvotes: 2
Views: 9198
Reputation: 776
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;
}
}
Upvotes: 0
Reputation: 6111
What about a nice simple
RedirectToAction("ActionName");
Hope this helps?
Upvotes: 0
Reputation: 3490
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:
override System.Web.Mvc.MvcHandler's ProcessRequest method. This is a nuclear option in my opinion.
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.
Upvotes: 0
Reputation: 1038710
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 ) );
}
Upvotes: 2
Reputation: 116977
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?
Upvotes: 1