获取给定控制器和操作的路由模板

时间:2018-09-24 13:08:38

标签: asp.net angularjs routing

我正在构建一个带有angularjs前端和ASP.NET后端的应用程序。为了使我的角度服务能够发出请求,我需要构造一个给定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}"

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

我的第一个尝试是使用反射来获取所有action方法,然后调用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模板?我是从根本上以错误的方式解决了这个问题吗?谢谢。

1 个答案:

答案 0 :(得分:0)

最后,我决定直接从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;
}

在任何控制器上我都想返回网址模板:

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

    return Json(routeTemplates);
}