如何在不使用属性路由的路由属性上指定名称的情况下生成 Web Api 2 URL?

2024-03-31

我已将 ASP.NET MVC5 应用程序配置为使用 WebApi 的 AttributeRouting:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.MapHttpAttributeRoutes();
    }
}

我有一个ApiController如下:

[RoutePrefix("api/v1/subjects")]
public class SubjectsController : ApiController
{
    [Route("search")]
    [HttpPost]
    public SearchResultsViewModel Search(SearchCriteriaViewModel criteria)
    {
        //...
    }
}

我想生成 WebApi 控制器操作的 URL,而无需指定显式路由名称。

根据CodePlex 上的此页面 https://aspnetwebstack.codeplex.com/wikipage?title=Attribute%20routing%20in%20Web%20API,所有 MVC 路由都有一个不同的名称,即使未指定也是如此。

在没有指定路由名称的情况下,Web API 将生成一个 默认路由名称。如果只有一个属性路由 特定控制器上的操作名称,路由名称将采用 形式“控制器名称.操作名称”。如果有多个属性 在该控制器上具有相同的操作名称,后缀会添加到 区分路由:“Customer.Get1”、“Customer.Get2”。

在 ASP.NET 上 http://www.asp.net/web-api/overview/web-api-routing-and-actions/attribute-routing-in-web-api-2#route-names,它没有确切说明默认的命名约定是什么,但它确实表明每个路由都有一个名称。

在 Web API 中,每条路线都有一个名字。路线名称很有用 生成链接,以便您可以在 HTTP 响应中包含链接。

基于这些资源和StackOverflow 用户 Karhgath 的回答 https://stackoverflow.com/a/21186709/1195273,我被引导相信以下内容会生成我的 WebApi 路由的 URL:

@(Url.RouteUrl("Subjects.Search"))

但是,这会产生错误:

在路线中找不到名为“Subjects.Search”的路线 收藏。

我根据在 StackOverflow 上找到的其他答案尝试了一些其他变体,但没有成功。

@(Url.Action("Search", "Subjects", new { httproute = "" }))

@(Url.HttpRouteUrl("Search.Subjects", new {}))

事实上,即使在属性中提供路由名称似乎也只适用于:

@(Url.HttpRouteUrl("Search.Subjects", new {}))

其中“Search.Subjects”被指定为 Route 属性中的路由名称。

我不想被迫为我的路线指定唯一的名称。

如何生成 WebApi 控制器操作的 URL,而无需在 Route 属性中显式指定路由名称?

默认路由命名方案是否可能已更改或在 CodePlex 中记录不正确?

是否有人对检索已使用属性路由设置的路由的 URL 的正确方法有所了解?


使用变通办法通过检查 Web Api 来查找路线IApiExplorer与强类型表达式一起,我能够生成 WebApi2 URL,而无需指定Name on the Route属性与属性路由。

我创建了一个帮助程序扩展,它允许我使用强类型表达式UrlHelper在 MVC 剃刀中。这对于从视图中解析 MVC 控制器的 URI 非常有效。

<a href="@(Url.Action<HomeController>(c=>c.Index()))">Home</a>
<li>@(Html.ActionLink<AccountController>("Sign in", c => c.Signin(null)))</li>
<li>@(Html.ActionLink<AccountController>("Create an account", c => c.Signup(), htmlAttributes: null))</li>
@using (Html.BeginForm<ToolsController>(c => c.Track(null), FormMethod.Get, htmlAttributes: new { @class = "navbar-form", role = "search" })) {...}    

我现在有一个视图,我正在尝试使用淘汰赛将一些数据发布到我的 Web api 并且需要能够执行类似的操作

var targetUrl = '@(Url.HttpRouteUrl<TestsApiController>(c => c.TestAction(null)))';

这样我就不必对我的网址进行硬编码(魔术字符串)

我当前用于获取 Web API url 的扩展方法的实现是在以下类中定义的。

public static class GenericUrlActionHelper {
    /// <summary>
    /// Generates a fully qualified URL to an action method 
    /// </summary>
    public static string Action<TController>(this UrlHelper urlHelper, Expression<Action<TController>> action)
       where TController : Controller {
        RouteValueDictionary rvd = InternalExpressionHelper.GetRouteValues(action);
        return urlHelper.Action(null, null, rvd);
    }

    public const string HttpAttributeRouteWebApiKey = "__RouteName";
    public static string HttpRouteUrl<TController>(this UrlHelper urlHelper, Expression<Action<TController>> expression)
       where TController : System.Web.Http.Controllers.IHttpController {
        var routeValues = expression.GetRouteValues();
        var httpRouteKey = System.Web.Http.Routing.HttpRoute.HttpRouteKey;
        if (!routeValues.ContainsKey(httpRouteKey)) {
            routeValues.Add(httpRouteKey, true);
        }
        var url = string.Empty;
        if (routeValues.ContainsKey(HttpAttributeRouteWebApiKey)) {
            var routeName = routeValues[HttpAttributeRouteWebApiKey] as string;
            routeValues.Remove(HttpAttributeRouteWebApiKey);
            routeValues.Remove("controller");
            routeValues.Remove("action");
            url = urlHelper.HttpRouteUrl(routeName, routeValues);
        } else {
            var path = resolvePath<TController>(routeValues, expression);
            var root = getRootPath(urlHelper);
            url = root + path;
        }
        return url;
    }

    private static string resolvePath<TController>(RouteValueDictionary routeValues, Expression<Action<TController>> expression) where TController : Http.Controllers.IHttpController {
        var controllerName = routeValues["controller"] as string;
        var actionName = routeValues["action"] as string;
        routeValues.Remove("controller");
        routeValues.Remove("action");

        var method = expression.AsMethodCallExpression().Method;

        var configuration = System.Web.Http.GlobalConfiguration.Configuration;
        var apiDescription = configuration.Services.GetApiExplorer().ApiDescriptions
           .FirstOrDefault(c =>
               c.ActionDescriptor.ControllerDescriptor.ControllerType == typeof(TController)
               && c.ActionDescriptor.ControllerDescriptor.ControllerType.GetMethod(actionName) == method
               && c.ActionDescriptor.ActionName == actionName
           );

        var route = apiDescription.Route;
        var routeData = new HttpRouteData(route, new HttpRouteValueDictionary(routeValues));

        var request = new System.Net.Http.HttpRequestMessage();
        request.Properties[System.Web.Http.Hosting.HttpPropertyKeys.HttpConfigurationKey] = configuration;
        request.Properties[System.Web.Http.Hosting.HttpPropertyKeys.HttpRouteDataKey] = routeData;

        var virtualPathData = route.GetVirtualPath(request, routeValues);

        var path = virtualPathData.VirtualPath;

        return path;
    }

    private static string getRootPath(UrlHelper urlHelper) {
        var request = urlHelper.RequestContext.HttpContext.Request;
        var scheme = request.Url.Scheme;
        var server = request.Headers["Host"] ?? string.Format("{0}:{1}", request.Url.Host, request.Url.Port);
        var host = string.Format("{0}://{1}", scheme, server);
        var root = host + ToAbsolute("~");
        return root;
    }

    static string ToAbsolute(string virtualPath) {
        return VirtualPathUtility.ToAbsolute(virtualPath);
    }
}

InternalExpressionHelper.GetRouteValues检查表达式并生成RouteValueDictionary将用于生成 url。

static class InternalExpressionHelper {
    /// <summary>
    /// Extract route values from strongly typed expression
    /// </summary>
    public static RouteValueDictionary GetRouteValues<TController>(
        this Expression<Action<TController>> expression,
        RouteValueDictionary routeValues = null) {
        if (expression == null) {
            throw new ArgumentNullException("expression");
        }
        routeValues = routeValues ?? new RouteValueDictionary();

        var controllerType = ensureController<TController>();

        routeValues["controller"] = ensureControllerName(controllerType); ;

        var methodCallExpression = AsMethodCallExpression<TController>(expression);

        routeValues["action"] = methodCallExpression.Method.Name;

        //Add parameter values from expression to dictionary
        var parameters = buildParameterValuesFromExpression(methodCallExpression);
        if (parameters != null) {
            foreach (KeyValuePair<string, object> parameter in parameters) {
                routeValues.Add(parameter.Key, parameter.Value);
            }
        }

        //Try to extract route attribute name if present on an api controller.
        if (typeof(System.Web.Http.Controllers.IHttpController).IsAssignableFrom(controllerType)) {
            var routeAttribute = methodCallExpression.Method.GetCustomAttribute<System.Web.Http.RouteAttribute>(false);
            if (routeAttribute != null && routeAttribute.Name != null) {
                routeValues[GenericUrlActionHelper.HttpAttributeRouteWebApiKey] = routeAttribute.Name;
            }
        }

        return routeValues;
    }

    private static string ensureControllerName(Type controllerType) {
        var controllerName = controllerType.Name;
        if (!controllerName.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)) {
            throw new ArgumentException("Action target must end in controller", "action");
        }
        controllerName = controllerName.Remove(controllerName.Length - 10, 10);
        if (controllerName.Length == 0) {
            throw new ArgumentException("Action cannot route to controller", "action");
        }
        return controllerName;
    }

    internal static MethodCallExpression AsMethodCallExpression<TController>(this Expression<Action<TController>> expression) {
        var methodCallExpression = expression.Body as MethodCallExpression;
        if (methodCallExpression == null)
            throw new InvalidOperationException("Expression must be a method call.");

        if (methodCallExpression.Object != expression.Parameters[0])
            throw new InvalidOperationException("Method call must target lambda argument.");

        return methodCallExpression;
    }

    private static Type ensureController<TController>() {
        var controllerType = typeof(TController);

        bool isController = controllerType != null
               && controllerType.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)
               && !controllerType.IsAbstract
               && (
                    typeof(IController).IsAssignableFrom(controllerType)
                    || typeof(System.Web.Http.Controllers.IHttpController).IsAssignableFrom(controllerType)
                  );

        if (!isController) {
            throw new InvalidOperationException("Action target is an invalid controller.");
        }
        return controllerType;
    }

    private static RouteValueDictionary buildParameterValuesFromExpression(MethodCallExpression methodCallExpression) {
        RouteValueDictionary result = new RouteValueDictionary();
        ParameterInfo[] parameters = methodCallExpression.Method.GetParameters();
        if (parameters.Length > 0) {
            for (int i = 0; i < parameters.Length; i++) {
                object value;
                var expressionArgument = methodCallExpression.Arguments[i];
                if (expressionArgument.NodeType == ExpressionType.Constant) {
                    // If argument is a constant expression, just get the value
                    value = (expressionArgument as ConstantExpression).Value;
                } else {
                    try {
                        // Otherwise, convert the argument subexpression to type object,
                        // make a lambda out of it, compile it, and invoke it to get the value
                        var convertExpression = Expression.Convert(expressionArgument, typeof(object));
                        value = Expression.Lambda<Func<object>>(convertExpression).Compile().Invoke();
                    } catch {
                        // ?????
                        value = String.Empty;
                    }
                }
                result.Add(parameters[i].Name, value);
            }
        }
        return result;
    }
}

诀窍是获取操作的路由并使用它来生成 URL。

private static string resolvePath<TController>(RouteValueDictionary routeValues, Expression<Action<TController>> expression) where TController : Http.Controllers.IHttpController {
    var controllerName = routeValues["controller"] as string;
    var actionName = routeValues["action"] as string;
    routeValues.Remove("controller");
    routeValues.Remove("action");

    var method = expression.AsMethodCallExpression().Method;

    var configuration = System.Web.Http.GlobalConfiguration.Configuration;
    var apiDescription = configuration.Services.GetApiExplorer().ApiDescriptions
       .FirstOrDefault(c =>
           c.ActionDescriptor.ControllerDescriptor.ControllerType == typeof(TController)
           && c.ActionDescriptor.ControllerDescriptor.ControllerType.GetMethod(actionName) == method
           && c.ActionDescriptor.ActionName == actionName
       );

    var route = apiDescription.Route;
    var routeData = new HttpRouteData(route, new HttpRouteValueDictionary(routeValues));

    var request = new System.Net.Http.HttpRequestMessage();
    request.Properties[System.Web.Http.Hosting.HttpPropertyKeys.HttpConfigurationKey] = configuration;
    request.Properties[System.Web.Http.Hosting.HttpPropertyKeys.HttpRouteDataKey] = routeData;

    var virtualPathData = route.GetVirtualPath(request, routeValues);

    var path = virtualPathData.VirtualPath;

    return path;
}

现在如果我有以下 api 控制器

[RoutePrefix("api/tests")]
[AllowAnonymous]
public class TestsApiController : WebApiControllerBase {
    [HttpGet]
    [Route("{lat:double:range(-90,90)}/{lng:double:range(-180,180)}")]
    public object Get(double lat, double lng) {
        return new { lat = lat, lng = lng };
    }
}

到目前为止,当我测试它时,大部分都有效

@section Scripts {
    <script type="text/javascript">
        var url = '@(Url.HttpRouteUrl<TestsApiController>(c => c.Get(1,2)))';
        alert(url);
    </script>
}

I get /api/tests/1/2,这就是我想要的,我相信会满足您的要求。

请注意,对于具有以下路由属性的操作,它也会默认返回到 UrlHelperName.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在不使用属性路由的路由属性上指定名称的情况下生成 Web Api 2 URL? 的相关文章

  • 分层架构中的异常处理

    我们正在分层设计中重构 当然还有重新设计 我们的服务 我们有服务操作层 BLL 网络抽象层 gt 处理网络代理 数据抽象层 但我们对我们的异常处理策略有点困惑 我们不想向外界透露太多 BLL 的信息 从其他层到bll就可以了 我们不想让 t
  • 对静态成员变量的未定义引用

    我有一个有静态成员的类 它也是我的程序中其他几个类的基类 这是它的头文件 ifndef YARL OBJECT HPP define YARL OBJECT HPP namespace yarlObject class YarlObject
  • C++ 返回值、引用、const 引用

    你能向我解释一下返回值 值引用和值常量引用之间的区别吗 Value Vector2D operator const Vector2D vector this gt x vector x this gt y vector y return t
  • Linux 使用 boost asio 拒绝套接字绑定权限

    我在绑定套接字时遇到问题 并且以用户身份运行程序时权限被拒绝 这行代码会产生错误 acceptor new boost asio ip tcp acceptor io boost asio ip tcp endpoint boost asi
  • 如何启动异步任务对象

    我想开始收集Task同时处理对象并等待所有对象完成 下面的代码显示了我想要的行为 public class Program class TaskTest private Task createPauseTask int ms works w
  • 操作/Lambda 表达式内存管理问题

    我将一个操作存储在局部变量中 然后在该局部变量超出范围后使用 使用前是否有被清理的危险 这是一个例子 public List GetMaps Action
  • 宏可以按参数数量重载吗?

    如何this https stackoverflow com q 9183993 153285工作 如何实现 C99 C 11 可变参数宏以仅根据为其提供多少个参数来扩展到不同的事物 编辑 请参阅末尾以获得现成的解决方案 要获得重载的宏 首
  • 在 C# 中调用事件处理程序

    我一直在尝试学习如何在 C 中使用事件处理程序 但我无法弄清楚 handler this e 在以下代码中的作用 public event EventHandler ThresholdReached protected virtual vo
  • C++:将模板参数的模板类型成员添加为好友的正确语法?

    我有一个带有模板类型参数 tTRAIT 的类 我想加一个模板为好友type member aliastTRAIT 但我无法弄清楚语法 这可能吗 template
  • StreamReader,C#,peek

    我有一个 StreamReader 它偶尔会检查它是否有更多内容可以从简单的文本文件中读取 它使用 peek 属性 问题是 当我使用 peek 时 位置发生了变化 尽管不应该发生 FileStream m fsReader new File
  • 有没有办法将 boost::json::serializer 切换为美化输出?

    Using boost json serializer如中的示例所示文档 快速查看 http vinniefalco github io doc json json usage quick look html以紧凑格式保存 json tre
  • Qt QML 数据模型似乎不适用于 C++

    我一直在使用中的示例http doc qt digia com 4 7 qdeclarativemodels html http doc qt digia com 4 7 qdeclarativemodels html这是 QML 声明性数
  • 本地主机上的 .net HTTP_X_FORWARDED_FOR NULL

    抱歉 如果其他地方已经回答了这个问题 我找不到它 如果没有 我会尝试查找访问过该站点的机器的原始 IP 根据我的基本理解 变量HTTP X FORWARDED FOR无论代理和其他过滤器如何 都会显示用户的 IP 如果这是真的 我正在尝试对
  • 使用 QGraphicsScene 实现流畅的动画

    我希望我的问题并不总是同样的问题 我有一个 QGraphicsScene 它的项目是一些 QGraphicsPixmap 我用一个计时器来移动它们 每秒 SetX 10 我设置 10是因为窗口大100 使用这个解决方案我的动画不流畅 我想我
  • ASP.NET MVC 中 ModelState.AddModelError 中的关键参数有什么意义?

    我在我的控制器中添加了验证检查来修改ModelState如果验证失败 例如 private bool ValidateMoney string raw string name decimal min decimal max try var
  • TreeView:仅在子节点中存在复选框

    我需要一个树视图控件 根节点没有复选框 只有图像 所有子节点都有一个复选框 图像 C net 2 0 winforms 不是 wpf WinForms树视图默认不支持混合复选框 非复选框节点 您可以在树视图上全局启用复选框 并使用以下命令在
  • 非静态类中的静态方法和静态类中的静态方法有什么区别?

    我有两个班级A级和B级 static class ClassA static string SomeMethod return I am a Static Method class ClassB static string SomeMeth
  • OpenGL 计算着色器调用

    我有一个与新计算着色器相关的问题 我目前正在研究粒子系统 我将所有粒子存储在着色器存储缓冲区中 以便在计算着色器中访问它们 然后我派遣一个一维工作组 define WORK GROUP SIZE 128 shaderManager gt u
  • 如何在 C# 中以编程方式创建柔和的颜色?

    根据所需的颜色数量均匀分布地生成它们 如果指定的计数为 8 则看起来像这样 List
  • 如何在用户空间程序中使用内核 libcrc32c (或相同的函数)?

    我想在我自己的用户空间程序中进行一些 CRC 检查 我发现内核加密库已经在系统中 并且支持 SSE4 2 我尝试直接 include

随机推荐

  • 设置对象彼此相等(java)

    所以我有一个名为 Person 的类 看起来像这样 public class Person private String personName public String toString return personName public
  • 使用 AutoFac 注入通用类型参数

    我想我真的很困惑我能用 AutoFac 做什么 有人可以让我走上正轨吗 我有一个基本类型 class PersonBase public string SaySomething return I am base 我派生出两个具体的类 cla
  • 自定义 ViewGroup 焦点处理

    假设我有一个可聚焦的自定义 ViewGroup 并且有一些可聚焦的子视图 适用于 Android 机顶盒的自定义垂直菜单 应在遥控器上做出反应 每当自定义 ViewGroup 获得焦点时 我需要将焦点传递给某些子视图 I set desce
  • 布局渲染后如何初始化 jQuery 对象?

    我想定义一些变量 以便在我的应用程序布局呈现后立即用于确定 jQuery 选择器的范围 我尝试用以下方式定义它们 Meteor startup function Define variables 但它不起作用 对象是空的 这是我声明我的应用
  • Pvr 在 cocos2d 3.2 版本中翻转:从 2.1 移植时的困境

    我目前正在将一个游戏从cocos2d 2 1版本移植到3 2版本 我们有超过 3600 个未翻转的 pvr gz 文件 例如在 2 1 下生成和工作的文件 我们使用TexturePacker测试了pvr图像的flipY选项 并且确实有足够的
  • Apache Ignite - (jvm-pause- detector-worker) JVM 暂停时间可能过长:

    使用 apache ignite 2 6 后 我在 JBoss 启动和停止时不断收到此警告 您能否帮我知道什么错误的配置会导致此警告 如果您需要更多详细信息 请告诉我 最有可能的是 JVM 正在经历长时间的垃圾收集暂停 可以配置详细的GC日
  • Keras 中的 5 层 DNN 使用 GPU 训练速度较慢

    我在 Keras 1 2 中使用 tensorflow gpu 作为后端编写了一个 5 层密集网络 并在我的 MacBookPro CPU 和 AWS 中的 P2 xlarge 实例 K80 启用 cuda 中对其进行训练 令人惊讶的是 我
  • 在事件中对 HTML 表格单元格(或整行)的背景颜色进行动画处理

    我有一张桌子 上面有一个菜单 食品 有几行和几列 第二列包含食品的链接 当用户点击它时 该商品已添加到购物车中 我想给用户一些点击和添加的视觉反馈 确实有效 我已经有一个用于添加链接的点击处理程序 商品点击进入购物车 一个简单的alert
  • 如何计算从 Shiny 中的文本输入框获取的数据?

    ui code library shiny shinyUI Use a fluid Bootstrap layout fluidPage Generate a row with a sidebar sidebarLayout Define
  • 将日期转换为特定格式的字符在R中

    我需要映射 3 4 个具有不同日期格式的不同数据帧 我们如何将日期转换为以下格式 YYYY MM DD 转换为以下格式的字符 MMM YY 从字符串创建日期对象 如果您的列已采用日期格式 请跳过此操作 original date lt as
  • 防止 SliverAppBar 标题在 Flutter 中收缩时换行

    我有一个 Flutter 应用程序 它在 CustomScrollView 中使用 SliverAppBar SliverAppBar 的标题非常长 因此当 AppBar 在滚动时压缩时 标题开始换行几次 我想在不使用溢出的情况下防止这种行
  • 根据条件合并不同行的字段

    亚马逊为其市场客户提供 CSV 报告 其中包含有关您销售的每篇文章的信息 每篇文章有四行 如下所示 orderid amounttype amountdescription amount 305 2406165 0572365 ItemPr
  • 每次点击都会弹出新窗口

    我有一个弹出窗口 我使用下面的脚本打开它 每次点击时 我想打开新的弹出窗口 据我了解 为窗口指定唯一的名称将解决问题 在本例中为 SampleWindow 保持窗户独特性的最佳方法是什么 还有其他方法可以管理 javascript 弹出窗口
  • Orchard CMS 事务错误

    我们的 Orchard 网站上经常随机出现以下错误 当在页面之间快速导航时 这种情况似乎最为普遍 日志中没有其他错误 因此我不确定如何找出根本原因 我们没有对 ODBC 或类似的东西做任何特殊的事情 这是 google 在搜索有关 Orch
  • 使用 x64 汇编代码的基本输入

    我正在编写有关汇编中基本输入和输出的教程 我使用的是 64 位 Linux 发行版 Ubuntu 在教程的第一部分中 我讨论了基本输出并创建了一个简单的程序 如下所示 global start section text start mov
  • 从 gradle/groovy 中的路径创建目录结构

    我正在从 git 命令行输出在项目的 gradle 构建中实现 diff 包生成任务 目前我有一个方法可以给我一个已更改文件的列表git diff name only 我想做的是在一个新目录中创建一个与每个文件的路径匹配的目录结构 例如 输
  • 从Python中的日期字符串中删除时间戳

    我遇到了应该忽略日期字符串中的时间戳的情况 我尝试过以下命令 但没有成功 start variable used below is in AbsTime Ex 01MAY2017 11 45 and not a string start d
  • 是否有任何 AngularJS + ASP.NET-WebApi + OData + Breeze.js + Typescript 示例,或者有人尝试将这些示例结合起来

    我试图结合这些技术 但没有什么好的结果 因为实体框架元数据不会被breeze js消耗 即使是所有设置的配置 这也是一个有点棘手的情况 实际上没有这样的例子 所以这个是我的示例代码 它不能正常工作 但不知何故 也许有人会发现我的错误 并最终
  • 错误:数据库连接“Sqlite”丢失,或无法创建 Cakephp 2.9.0

    您好 我正在使用 phpdesktop 构建桌面应用程序 我遇到了这个问题Error Database connection Sqlite is missing or could not be created 使用时cake bake 我能
  • 如何在不使用属性路由的路由属性上指定名称的情况下生成 Web Api 2 URL?

    我已将 ASP NET MVC5 应用程序配置为使用 WebApi 的 AttributeRouting public static class WebApiConfig public static void Register HttpCo