使用 MVC.UrlHelper 从 MVC 视图中解析 WebApi 操作 Url

Resolve WebApi action Url from within MVC View using MVC.UrlHelper

我创建了一个辅助扩展,它允许我在 MVC razor 中使用 UrlHelper 强类型表达式。这非常适合从视图中解析我的 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" })) {...}    

我现在有一个视图,我正在尝试使用敲除 post 一些数据到我的网站 api 并且需要能够做这样的事情

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

这样我就不必硬编码我的 urls(魔术字符串)

我目前实现的获取网络的扩展方法 API url 定义如下。

private const string HttpRouteKey = "httproute";
public static string HttpRouteUrl<TController>(this UrlHelper urlHelper, Expression<Action<TController>> action)
   where TController : System.Web.Http.ApiController {
    RouteValueDictionary routeValues = InternalExpressionHelper.GetRouteValues(action);
    if (!routeValues.ContainsKey(HttpRouteKey)) {
        routeValues.Add(HttpRouteKey, true);
    }
    var scheme = urlHelper.RequestContext.HttpContext.Request.Url.Scheme;
    var route = urlHelper.Action(null, null, rvd, scheme);
    //I've also tried
    //var route = urlHelper.HttpRouteUrl(null, rvd);
    return route;
}

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

RouteValueDictionary

问题是我的 route 总是 returns null。以前关于 SO 和博客的问题表明,通过在字典中包含 httproute 将允许从 MVC.UrlHelper 路由集合中获取 WebApi Urls。

到目前为止 none 接受的答案似乎对我有用,因为我使用的是没有路由名称的属性路由。

当生成指向 Web API 路由的链接时,必须始终要求 RouteName 才能正常工作,因此我决定应用一个约定。

帮助程序扩展通过组合控制器名称和操作名称来确定 RouteName。

var routeName = string.Join("_", controllerName, actionName);

现在因为当从包含控制器名称和动作名称的表达式中提取信息时,它们也显示在 url 中。不是很漂亮

/api/tests/1/2?controller=TestsApi&amp;action=Get

因此它们已从路由值中删除。

routeValues.Remove("controller");
routeValues.Remove("action");

更新扩展方法现在看起来像这样

private const string HttpRouteKey = "httproute";
public static string HttpRouteUrl<TController>(this UrlHelper urlHelper, Expression<Action<TController>> action)
   where TController : System.Web.Http.Controllers.IHttpController {
    var routeValues = InternalExpressionHelper.GetRouteValues(action);
    if (!routeValues.ContainsKey(HttpRouteKey)) {
        routeValues.Add(HttpRouteKey, true);
    }

    var controllerName = routeValues["controller"] as string;
    routeValues.Remove("controller");
    var actionName = routeValues["action"] as string;
    routeValues.Remove("action");

    var routeName = string.Join("_", controllerName, actionName);

    var url = urlHelper.HttpRouteUrl(routeName, routeValues);

    return url;
}

现在我只需要确保,如果我希望我的操作可以从 MVC 或 Web API 生成 url/link,那么必须应用 RouteName公约 "{controller}_{action}"

[RoutePrefix("api/tests")]
[AllowAnonymous]
public class TestsApiController : WebApiControllerBase {
    [HttpGet]
    [Route("{lat:double:range(-90,90)}/{lng:double:range(-180,180)}", Name = "TestsApi_Get")]
    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>
}

我得到了/api/tests/1/2,这就是我想要的。