C# 相对于路径匹配路由

C# 相对于路径匹配路由,c#,asp.net-mvc,routes,url-routing,asp.net-mvc-routing,C#,Asp.net Mvc,Routes,Url Routing,Asp.net Mvc Routing,我希望任何以/templates/{filename}结尾的URL都可以使用路由属性映射到特定控制器,例如: public class TemplateController : Controller { [Route("templates/{templateFilename}")] public ActionResult Index(string templateFilename) { .... } } [Route("*/templates/

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

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

}
[Route("*/templates/{templateFilename}")]
这是可行的,但是引用此路由的链接是相对的

  • http://localhost/templates/t1
    --有效
  • http://localhost/foo/bar/templates/t2
    --中断(404)
我需要像这样的东西:

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

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

使用属性路由无法完成类似的任务。只有通过实现
iroutesconstraint
或子类化
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;
    }
}
用法 这将与以下URL匹配:

http://localhost/templates/t1
http://localhost/foo/bar/templates/t2
并将它们发送到
TemplateController.Index()
方法,最后一段作为
templateFilename
参数

注意:出于搜索引擎优化的目的,通常认为在多个URL上放置相同的内容不是一个好的做法。如果您这样做了,建议使用通知通知搜索引擎哪个URL是权威URL


.

我没有看到任何以
foo/bar/templates/
开头的代码。你的代码似乎做到了它所说的。你的URL中不能有文件路径-你的应用程序如何知道路由斜杠和目录树斜杠之间的区别?@Jakotheshadows我在我的web.config中有这个。。。据我所知,这仅仅意味着你可以提供一个html文件。这并不意味着允许您的路由参数包含斜杠,而不映射到带有这些斜杠的实际路由。任何以“templates/{thefiletemplateName}”结尾的url我希望映射到TemplateController。那我怎么办?谢谢你!这就是我所认为的情况。这是一项非常艰巨的工作,因为可以用RouteConstraint来完成。@ErikPhilips-试图用route约束来完成它,但似乎没有办法做到这一点,因为传入和传出URL是不同的。但如果你有一个更简洁的方法来实现这一点,这将是有趣的看到。。。你的意思是服务器端重定向还是302?@ErikPhilips?如果你走那条路线,你最终得到的路线只能匹配传入URL,但需要你定义第二条路线来生成传出URL。这是一个比简单创建
RouteBase
子类更复杂的配置,它可以控制双向路由(匹配传入URL并生成UI的URL)。如果您不需要生成URL来放在UI上,那么使用路由约束的代码就更少了,但它感觉有点破碎,因为每次使用它时,您都必须生成一个catch-all参数,否则路由约束将不起作用。