I\'m having a heck of a time figuring out how to properly implement my 404 redirecting.
If I use the following
_
Public Class BaseC
I have a C# solution, I hope it helps. I plagiarized some of this code, though for the life of me, I cannot find where I got it from. If anyone know, please let me know so I can add it to my comments.
This solution does not use reflection, but it looks at all the application errors (exceptions) and checks to see if it's a 404 error. If it is, then it just routes the current request to a different controller. Though I am not an expert in any way, I think this solution might be faster than reflection. Anyway, here's the solution and it goes into your Global.asax.cs,
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
// A good location for any error logging, otherwise, do it inside of the error controller.
Response.Clear();
HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "YourErrorController");
if (httpException != null)
{
if (httpException.GetHttpCode() == 404)
{
routeData.Values.Add("action", "YourErrorAction");
// We can pass the exception to the Action as well, something like
// routeData.Values.Add("error", exception);
// Clear the error, otherwise, we will always get the default error page.
Server.ClearError();
// Call the controller with the route
IController errorController = new ApplicationName.Controllers.YourErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
}
}
So the controller would be,
public class YourErrorController : Controller
{
public ActionResult YourErrorAction()
{
return View();
}
}
Why don't you just capture them with custom errors in your web.config file and avoid a bunch of reflection all together?
<customErrors mode="On">
<error statusCode="404" redirect="/Error/NotFound" />
</customErrors>
That's a very similar problem to mine, but I like your alternate approach.
I think the reflection as a dynamic filter might be too performance heavy, but I think I have a better way - you can filter allowed actions by a Regex:
// build up a list of known controllers, so that we don't let users hit ones that don't exist
var allMvcControllers =
from t in typeof(Global).Assembly.GetTypes()
where t != null &&
t.IsPublic &&
!t.IsAbstract &&
t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
typeof(IController).IsAssignableFrom(t)
select t.Name.Substring(0, t.Name.Length - 10);
// create a route constraint that requires the controller to be one of the reflected class names
var controllerConstraint = new
{
controller = "(" + string.Join("|", allMvcControllers.ToArray()) + ")"
};
// default MVC route
routes.MapRoute(
"MVC",
"{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional },
controllerConstraint);
// fall back route for unmatched patterns or invalid controller names
routes.MapRoute(
"Catch All",
"{*url}",
new { controller = "System", action = "NotFound" });
Then I add to this an additional method on my base Controller
:
protected override void HandleUnknownAction(string actionName)
{
this.NotFound(actionName).ExecuteResult(this.ControllerContext);
}
In this case BaseController.NotFound
handles the missing action on a valid controller.
So finally:
{site}/invalid
- found by new reflection based filter{site}/valid/notAnAction
- found by HandleUnknownAction
{site}/valid/action/id
- found by checks in code for the id (as before){site}/valid/action/id/extraPath
- found by not matching any route but the catch allI think that's all the 404 scenarios covered :-)