【问题标题】:Getting the route template for a given Controller and Action获取给定 Controller 和 Action 的路由模板
【发布时间】:2019-02-28 00:25:15
【问题描述】:

我正在构建一个带有 angularjs 前端和 ASP.NET 后端的应用程序。为了让我的 Angular 服务发出请求,我需要在给定 ASP.NET 控制器和操作列表的情况下构造一个 url 模板列表,然后我可以将其发送给客户端。

我正在寻找的结果类似于:

[RoutePrefix("my-controller")]
public class MyController : ControllerBase
{
    [Route("my-action/{arg1}/{arg2}")]
    public ActionResult MyAction(int arg1, string arg2) {
        // ...
    }
}

// Elsewhere in my code
GetRouteTemplate("MyController", "MyAction") // => "my-controller/my-action/{arg1}/{arg2}"

我不希望将这些硬编码到前端,因为对路由的任何更改都会破坏角度代码,所以我正在寻找一种生成它们的方法。

我的第一次尝试是使用反射来获取所有的动作方法,然后调用 Url.Action 来获取 url。我把它放在我的基本控制器类中:

protected Dictionary<String, String> GetUrlTemplates()
{
    var controllerName = this
        .GetType()
        .Name;

    var actionNames = this
        .GetType()
        .GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
        .Where(m => !m.IsDefined(typeof(NonActionAttribute), false))
        .Select(m => m.Name)
        .Distinct()
        .ToList();

    return actionNames 
        .ToDictionary(
            actionName => actionName,
            actionName => Url.Action(actionName, controllerName));
}

这对于不需要参数的任何操作都可以,但否则无法返回正确的路线。

我的下一个尝试是尝试将模板直接拉出 RouteTable:

protected Dictionary<String, String> GetUrlTemplates()
{
    var controllerName = this
        .GetType()
        .Name;

    var actions = this
        .GetType()
        .GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
        .Where(m => !m.IsDefined(typeof(NonActionAttribute), false))
        .Select(m => m.Name)
        .Distinct()
        .ToList();

    return RouteTable.Routes
        .OfType<LinkGenerationRoute>()
        .Select(lgr => new {
            url = lgr.Url,
            routeAction = lgr.DataTokens["MS_DirectRouteActions"][0]
        })
        .Where(o => o.routeAction.ControllerDescriptor.ControllerName == controllerName
            && actions.Contains(o.routeAction.ActionName))
        .ToDictionary(
            o => o.routeAction.ActionName,
            o => o.url);
}

这不起作用,因为 LinkGenerationRoute 是一个内部类,所以除非有另一种方法可以访问路由表中的这些值,否则这看起来也像死路一条。

这两种尝试都有点难看,而且似乎是错误的方法,但我看不出有任何其他方法可以解决。当然,为前端生成 url 模板是一项很常见的任务 - 在 ASP.NET 中获取 url 模板是否有“正确”的方法?我从根本上是不是以错误的方式处理这个问题?谢谢。

【问题讨论】:

    标签: asp.net angularjs routes


    【解决方案1】:

    最后我决定直接从 RoutePrefix 和 Route 属性中提取路由模板,这对于这个项目来说很好,因为所有路由都是这样配置的。我仍然觉得应该有一个更简单/不那么脆弱的解决方案 - 如果有人用更好的答案做出回应,我会接受它。

    这是我添加到基本控制器的方法:

    protected Dictionary<String, String> GetRouteTemplates(Type controllerType, params String[] actions)
    {
        var routePrefix = controllerType
            .GetCustomAttribute<RoutePrefixAttribute>()
            .Prefix;
    
        var routeTemplates = controllerType
            .GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
            .Where(m => !actions.Any() || actions.Contains(m.Name))
            .Where(m => !m.IsDefined(typeof(NonActionAttribute), false) && m.IsDefined(typeof(RouteAttribute), false))
            .Select(m => new {m.Name, m.GetCustomAttribute<RouteAttribute>().Template})
            .ToDictionary(r => r.Name, r => $"/{routePrefix}/{r.Template}");
    
        return routeTemplates;
    }
    

    在任何我想返回 Url 模板的控制器上:

    [Route("urls")]
    public JsonResult Urls(Int32 organisationId)
    {
        var routeTemplates = GetRouteTemplates(GetType());
    
        return Json(routeTemplates);
    }
    

    【讨论】:

    • 谢谢,我认为大多数情况下都可以。它总是可以更健壮,例如考虑控制器上的AreaRouteAttribute 等,但对我来说它已经足够有用了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-13
    • 1970-01-01
    • 2013-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多