c# - 相对于路径匹配路由

标签 c# asp.net-mvc routes url-routing asp.net-mvc-routing

我希望任何以 /templates/{filename} 结尾的 URL 使用路由属性映射到特定 Controller ,例如:

public class TemplateController : Controller
{
    [Route("templates/{templateFilename}")]
    public ActionResult Index(string templateFilename)
    {
        ....
    }

}

这是可行的,但是引用该路线的链接是相对的

  • http://localhost/templates/t1 -- 有效
  • http://localhost/foo/bar/templates/t2 -- 中断 (404)

我需要类似的东西:

[Route("*/templates/{templateFilename}")]

最佳答案

您无法通过属性路由完成类似的事情。只能通过实现IRouteConstraint或子类化RouteBase来进行高级路由匹配。

在这种情况下,子类化RouteBase会更简单。这是一个例子:

public class EndsWithRoute : RouteBase
{
    private readonly Regex urlPattern;
    private readonly string controllerName;
    private readonly string actionName;
    private readonly string prefixName;
    private readonly string parameterName;

    public EndsWithRoute(string controllerName, string actionName, string prefixName, string parameterName)
    {
        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(prefixName))
            throw new ArgumentException($"'{nameof(prefixName)}' is required.");
        if (string.IsNullOrWhiteSpace(parameterName))
            throw new ArgumentException($"'{nameof(parameterName)}' is required.");

        this.controllerName = controllerName;
        this.actionName = actionName;
        this.prefixName = prefixName;
        this.parameterName = parameterName;
        this.urlPattern = new Regex($"{prefixName}/[^/]+/?$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
    }

    public override RouteData GetRouteData(HttpContextBase httpContext)
    {
        var path = httpContext.Request.Path;

        // Check if the URL pattern matches
        if (!urlPattern.IsMatch(path, 1))
            return null;

        // Get the value of the last segment
        var param = path.Split('/').Last();

        var routeData = new RouteData(this, new MvcRouteHandler());

        //Invoke MVC controller/action
        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;

        return routeData;
    }

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
    {
        object controllerObj;
        object actionObj;
        object parameterObj;

        values.TryGetValue("controller", out controllerObj);
        values.TryGetValue("action", out actionObj);
        values.TryGetValue(parameterName, out parameterObj);

        if (controllerName.Equals(controllerObj.ToString(), StringComparison.OrdinalIgnoreCase) 
            && actionName.Equals(actionObj.ToString(), StringComparison.OrdinalIgnoreCase)
            && !string.IsNullOrEmpty(parameterObj.ToString()))
        {
            return new VirtualPathData(this, $"{prefixName}/{parameterObj.ToString()}".ToLowerInvariant());
        }
        return null;
    }
}

用法

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

        routes.Add(new EndsWithRoute(
            controllerName: "Template",
            actionName: "Index",
            prefixName: "templates",
            parameterName: "templateFilename"));

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

这将匹配这些 URL:

http://localhost/templates/t1
http://localhost/foo/bar/templates/t2

并将它们都发送到 TemplateController.Index() 方法,最后一段作为 templateFilename 参数。

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 Core .

关于c# - 相对于路径匹配路由,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49174973/

相关文章:

c# - 不使用 XAML 进行绑定(bind) [WPF]

c# - 从蛋糕脚本中将警告消息记录在文本文件中

c# - 在 MVC 中清除 View 引擎会破坏站点地图面包屑样式

java - 使用 Java 将消息从不同的 IP 地址发送到单个服务器

c# - AutoMapper - 如何在单个属性上使用类型转换器

c# - Azure API 身份验证

c# - 在 asp.net mvc 的同一 View 中列出和创建

c# - 总是在 MVC 中使用 ViewModel 模式?

php - 如何使用 PHP 路由和 .htaccess 包含 CSS/JS 和其他文件

iphone - 如何使用 google direction apis 或 iPhone 应用程序的其他一些 apis 比较两条路线