MVC Dynamic Routes

2019-03-31 06:42发布

问题:

I would like to create dynamic urls that route to controller actions with an Id value. I've created the following route using a catch-all parameter

routes.MapRoute(
            "RouteName",                                                 
            "{id}/{*Url}",
            new { controller = "Controller", action = "Action", id = "" }
        );

This works as expected and allows me to use the following Urls:

"http://website.com/1/fake/url/path" (1 being the id that gets passed to the action method)

Does anyone know a way to achieve it this way instead without creating my own http module?:

"http://website.com/fake/url/path/1"

Thanks - Mark

回答1:

That's a really difficult one, for me anyway.

Given the following route:

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

Your controller and supporting classes would be something like this:

[HandleError]
public class HomeController : Controller
{
    public ActionResult Index([ModelBinder(typeof(IndexReqquestBinder))] IndexRequest request)
    {
        ViewData["Title"] = "Home Page";
        ViewData["Message"] = String.Format("We're looking at ID: {0}", request.ID);

        return View();
    }
}

public class IndexRequest
{
    public Int32 ID { get; set; }
    public IndexRequest(Int32 id)
    {
        this.ID = id;
    }
}

public class IndexReqquestBinder : IModelBinder
{
    public ModelBinderResult BindModel(ModelBindingContext bindingContext)
    {
        if ( null != bindingContext.RouteData.Values["token"] ) {
            foreach ( String v in bindingContext.RouteData.Values["token"].ToString().Split('/') ) {
                Int32 id = 0;
                if ( Int32.TryParse(v, out id) ) {
                    return new ModelBinderResult(new IndexRequest(id));
                }
            }

        }
        return new ModelBinderResult(new IndexRequest(0));
    }
}


回答2:

Routes redirection is based on route entry in the route table. It must be in systematic way. For instance if you have route as "customurl/id" after {controller}/{action}/{id}(default of mvc) when you will enter "customurl" in the url box it will take it as default route and no page found exception will occur. So if you wan to use custom route then first remove the default route. I do this like.

RouteCollection routes = RouteTable.Routes;
                    if (routes["rname"] != null)
                    {
                        RouteTable.Routes.Remove(routes["rname"]);
                    }
                         routes.Remove(routes["Default"]);                           
                        routes.MapRoute(
                            name: "newname",
                            url: url + "/{customId}",
                            defaults: new { controller = "Search", action = "Index", customId = UrlParameter.Optional }
                            );

                        //default route value
                        routes.MapRoute(
                            name: "Default",
                            url: "{controller}/{action}/{id}",
                            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
                            );


回答3:

I would suggest overriding of DefaultControllerFactory

> public class CustomControllerFactory : DefaultControllerFactory
>   {
> public override IController
> CreateController(System.Web.Routing.RequestContext
> requestContext, string controllerName)
>         {
>             try
>             {
>                 return base.CreateController(requestContext,
> controllerName);
>             }
>             catch (Exception exception)
>             {
>                 // collect route data
>                 string id = (string)requestContext.RouteData.Values["id"];
>                 string action = (string)requestContext.RouteData.Values["action"];
>                 // set full path as routing "page" parameter
>                 VirtualPathData path = requestContext.RouteData.Route.GetVirtualPath(requestContext,
> requestContext.RouteData.Values);
>                 requestContext.RouteData.Values["id"]
> = path.VirtualPath; // or anything you need
>                 // use controller page by default
>                 controllerName = "MyController";
>                 // set basuc routing data
>                 requestContext.RouteData.Values["controller"]
> = controllerName;
>                 requestContext.RouteData.Values["action"]
> = "index";
>                 // call base method to create controller
>                 return base.CreateController(requestContext,
> controllerName);
>             }
>         }
>}

And than just register this as default controller factory in global.asax.cs file

protected void Application_Start()
        {
            ControllerBuilder.Current.SetControllerFactory(new MyNameSpace.CustomControllerFactory ());
            RegisterRoutes(RouteTable.Routes); // this already exists by default
        }