Ignore first segments in ASP.NET MVC Core routing

后端 未结 1 566
借酒劲吻你
借酒劲吻你 2021-01-22 05:15

I\'m looking for a route definition that is able to match routes like:

  • /segment/xxxx/def
  • /segment/.../xxxx/def
  • /s
相关标签:
1条回答
  • 2021-01-22 05:44

    You can use a custom IRouter combined with a regular expression to do advanced URL matching such as this.

    public class EndsWithRoute : IRouter
    {
        private readonly Regex urlPattern;
        private readonly string controllerName;
        private readonly string actionName;
        private readonly string parameterName;
        private readonly IRouter handler;
    
        public EndsWithRoute(string controllerName, string actionName, string parameterName, IRouter handler)
        {
            if (string.IsNullOrWhiteSpace(controllerName))
                throw new ArgumentException($"'{nameof(controllerName)}' is required.");
            if (string.IsNullOrWhiteSpace(actionName))
                throw new ArgumentException($"'{nameof(actionName)}' is required.");
            if (string.IsNullOrWhiteSpace(parameterName))
                throw new ArgumentException($"'{nameof(parameterName)}' is required.");
            this.controllerName = controllerName;
            this.actionName = actionName;
            this.parameterName = parameterName;
            this.handler = handler ??
                throw new ArgumentNullException(nameof(handler));
            this.urlPattern = new Regex($"{actionName}/[^/]+/?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
        }
    
        public VirtualPathData GetVirtualPath(VirtualPathContext context)
        {
            var controller = context.Values.GetValueOrDefault("controller") as string;
            var action = context.Values.GetValueOrDefault("action") as string;
            var param = context.Values.GetValueOrDefault(parameterName) as string;
    
            if (controller == controllerName && action == actionName && !string.IsNullOrEmpty(param))
            {
                return new VirtualPathData(this, $"{actionName}/{param}".ToLowerInvariant());
            }
            return null;
        }
    
        public async Task RouteAsync(RouteContext context)
        {
            var path = context.HttpContext.Request.Path.ToString();
    
            // Check if the URL pattern matches
            if (!urlPattern.IsMatch(path, 1))
                return;
    
            // Get the value of the last segment
            var param = path.Split('/').Last();
    
            //Invoke MVC controller/action
            var routeData = context.RouteData;
    
            routeData.Values["controller"] = controllerName;
            routeData.Values["action"] = actionName;
            // Putting the myParam value into route values makes it
            // available to the model binder and to action method parameters.
            routeData.Values[parameterName] = param;
    
            await handler.RouteAsync(context);
        }
    }
    

    Usage

    app.UseMvc(routes =>
    {
        routes.Routes.Add(new EndsWithRoute(
            controllerName: "Home", 
            actionName: "About", 
            parameterName: "myParam", 
            handler: routes.DefaultHandler));
    
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
    

    This route is parameterized to allow you to pass in the controller, action, and parameter names that correspond to the action method being called.

    public class HomeController : Controller
    {
        public IActionResult About(string myParam)
        {
            ViewData["Message"] = "Your application description page.";
    
            return View();
        }
    }
    

    It would take some more work to make it match any action method name and be able to build the URL again with that action method name. But this route will allow you to add additional action names by registering it more than one time.

    NOTE: For SEO purposes, it is generally not considered a good practice to put the same content on multiple URLs. If you do this, it is recommended to use a canonical tag to inform the search engines which of the URLs is the authoritative one.

    See this to accomplish the same in ASP.NET MVC (prior to ASP.NET Core).

    0 讨论(0)
提交回复
热议问题