Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with creating two routes which won't generate 404 error in ASP.NET MVC

I'm trying to build my tutorial project with routing. My main objective is to build two routes which won't generate 404 error in any case. By this I mean that if the path is wrong I want routing to use /Home/Index path. I have two following routes -

    routes.MapRoute("Default", "{controller}/{action}", 
                        new {controller = "Home", action = "Index"}
                        );

    routes.MapRoute("Second", "{*catchall}",
                        new {controller = "Home", action = "Index", id = UrlParameter.Optional}
                        );

It works fine when I use nonexistent path which doesn't matches the first route, like this -

Nonexistent path 1

But if it does, then I have the following -

enter image description here

or

enter image description here

I understand the reason why it happens. However at the present moment, I only managed to find 'some sort' of a solution. Adding the following code to web.config file -

<customErrors mode="On">
      <error statusCode="404" redirect="~/Home/Index"/>
</customErrors>

However, I don't think that it is the best way to solve this problem. Because, as far as I can understand, it simply catches all errors and redirects it to the correct path, without actually using routing. So I don't think I need this global handling.

So could somebody please give me a hint or provide me with a good solution to my problem. Thank you.

like image 843
renchan Avatar asked Nov 09 '22 04:11

renchan


1 Answers

Well, you didn't really define what "wrong" routing is, so here is my definition:

  1. The controller or action does not exist in the project.
  2. If an "id" is passed, it must exist on the action method.

Routes

I used constraints to do this. AFAIK, it is not possible to use a constraint on an optional parameter. This means that in order to make the id parameter optional, you need 3 routes.

routes.MapRoute(
   name: "DefaultWithID",
   url: "{controller}/{action}/{id}",
   defaults: new { controller = "Home", action = "Index" },
   constraints: new { action = new ActionExistsConstraint(), id = new ParameterExistsConstraint() }
);

routes.MapRoute(
   name: "Default",
   url: "{controller}/{action}",
   defaults: new { controller = "Home", action = "Index" },
   constraints: new { action = new ActionExistsConstraint() }
);

routes.MapRoute(
    name: "Second",
    url: "{*catchall}",
    defaults: new { controller = "Home", action = "Index" }
);

ActionExistsConstraint

public class ActionExistsConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (routeDirection == RouteDirection.IncomingRequest)
        {
            var action = values["action"] as string;
            var controller = values["controller"] as string;

            var thisAssembly = this.GetType().Assembly;

            Type[] types = thisAssembly.GetTypes();

            Type type = types.Where(t => t.Name == (controller + "Controller")).SingleOrDefault();

            // Ensure the action method exists
            return type != null && type.GetMethod(action) != null;
        }

        return true;
    }
}

ParameterExistsConstraint

public class ParameterExistsConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
    {
        if (routeDirection == RouteDirection.IncomingRequest)
        {
            var action = values["action"] as string;
            var controller = values["controller"] as string;

            var thisAssembly = this.GetType().Assembly;

            Type[] types = thisAssembly.GetTypes();

            Type type = types.Where(t => t.Name == (controller + "Controller")).SingleOrDefault();
            var method = type.GetMethod(action);

            if (type != null && method != null)
            {
                // Ensure the parameter exists on the action method
                var param = method.GetParameters().Where(p => p.Name == parameterName).FirstOrDefault();
                return param != null;
            }
            return false;
        }

        return true;
    }
}
like image 180
NightOwl888 Avatar answered Nov 14 '22 21:11

NightOwl888