简体   繁体   中英

Asp.net Mvc Route By Only Slug

I'm using ASP.NET MVC 4 with C#. I'm using areas and it's named like "Admin"

Here is my route config;

public static class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

        routes.MapRoute(name: "PageBySlug",
                        url: "{slug}",
                        defaults: new {controller = "Home", action = "RenderPage"},
                        constraints: new {slug = ".+"});

        routes.MapRoute(name: "Default",
                        url: "{controller}/{action}/{id}",
                        defaults: new {controller = "Home", action = "Index", id = UrlParameter.Optional},
                        namespaces: new[] { "Web.Frontend.Controllers.Controllers" });

    }
}

I generated frontend page links like; "products/apple-iphone" So I want to call them like this.

But the error is: The code can't get the controller / action method. I used frontend page links like;

@Html.ActionLink(linkItem.Title, "RenderPage", routeValues: new {controller = "Home", slug = linkItem.PageSlug})
@Html.RouteLink(linkItem.Title, routeName: "PageBySlug", routeValues: new { controller = "Home", action = "RenderPage", slug = linkItem.PageSlug })
<a href="@Url.Action("RenderPage", "Home", new {slug = linkItem.PageSlug})">@linkItem.Title</a>
<a href="@Url.RouteUrl("PageBySlug", new { controller = "Home", action = "RenderPage", slug = linkItem.PageSlug})">@linkItem.Title</a>

They are rendering url links like; http://localhost:1231/products/apple-iphone It's like what I want. But when I click any link, asp.net mvc gives me this error:

Server Error in '/' Application.

The resource cannot be found.

Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.

Requested URL: /products/apple-iphone

Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.1069.1

Here is my controller;

namespace Web.Frontend.Controllers
{
    public class HomeController : BaseFrontendController
    {
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult RenderPage(string slug)
        {
            return View();
        }
    }   
}

So how can I catch every link request like this combined slug and turn my coded view ?

The problem is, When you request products/iphone , the routing engine don't know whether you meant the slug "products/iphone" or the controller "products" and action method "iphone".

You can write a custom route constraint to take care of this. This constraint will check whether the slug part of the urls is a valid controller or not, if yes,the controller action will be executed.

public class SlugConstraint : IRouteConstraint
{
    public bool Match(HttpContextBase httpContext, Route route, string parameterName,
                       RouteValueDictionary values, RouteDirection routeDirection)
    {
        var asm = Assembly.GetExecutingAssembly();
        //Get all the controller names
        var controllerTypes = (from t in asm.GetExportedTypes()
                               where typeof(IController).IsAssignableFrom(t)
                               select t.Name.Replace("Controller", ""));
        var slug = values["slug"];
        if (slug != null)
        {

            if (controllerTypes.Any(x => x.Equals(slug.ToString(),
                                                      StringComparison.OrdinalIgnoreCase)))
            {
                return false;
            }
            else
            {
                var c = slug.ToString().Split('/');
                if (c.Any())
                {
                    var firstPart = c[0];
                    if (controllerTypes.Any(x => x.Equals(firstPart, 
                            StringComparison.OrdinalIgnoreCase)))
                    {
                        return false;
                    }
                }
            }
            return true;
        }
        return false;
    }
}

Now use this route constraint when you register your custom route definition for the slug. make sure you use { *slug} in the route pattern. The * indicates it is anything(Ex : "a/b/c")(Variable number of url segments- more like a catch all)

routes.MapRoute(name: "PageBySlug",
                            url: "{*slug}",
                            defaults: new { controller = "Home", action = "RenderPage" },
                      constraints: new { slug = new SlugConstraint() }    
                    , namespaces: new string[] { "Web.Frontend.Controllers.Controllers" });

routes.MapRoute(
             "Default",
             "{controller}/{action}/{id}",
            new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            , new string[] { "Web.Frontend.Controllers.Controllers" });

you can provide only this type of link

<a href="@Url.RouteUrl("PageBySlug", new {slug = linkItem.PageSlug})">@linkItem.Title</a>

Because Routetable find your route using Route name provided by you. so controller name and action name is not necessary.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM