Url.Action 为带有 Route 属性的 web api 操作返回不正确的 URL

Url.Action returning incorrect URL for webapi action with Route attrubute

我对 Url.Action();

的行为有疑问

我有一个 webapi,其中所有控制器都需要显式路由前缀属性,所有操作都需要路由属性。

我在 WebApiConfig.cs

中注册了我的路线
 var constraintResolver = new DefaultInlineConstraintResolver()
            {
                ConstraintMap =
                {
                    ["apiVersion"] = typeof( ApiVersionRouteConstraint )
                }
            };
            config.MapHttpAttributeRoutes(constraintResolver);

我目前已经注释掉了下面这行,但是(因为)它并没有改变不正确的行为:

  //config.Routes.MapHttpRoute(name: "DefaultApi", 
 //routeTemplate: "api/v{version:apiVersion}/{controller}/{action}/{id}", defaults: new {id = RouteParameter.Optional});
 

我的控制器如下所示:

 [RoutePrefix("api/v{version:apiVersion}/programs")]
 public class ProgramsController : ApiController
 {
    [HttpGet, Route("{telemetryKey}/versions/latest")]
    public async Task<LatestVersionResponse> GetLatestVersionInfo(Guid telemetryKey)
    {
        // serious business logic
    }

 }

我希望 '@Url.Action("GetLatestVersionInfo", "Programs", new { telemetryKey = Guid.Parse("43808405-afca-4abb-a92a-519489d62290") })'

应该return/api/v1/programs/43808405-afca-4abb-a92a-519489d62290/versions/latest

然而,我得到的是 /Programs/GetLatestVersionInfo?telemetryKey=43808405-afca-4abb-a92a-519489d62290。所以,我的路由前缀和路由属性被忽略了。

Swagger 正确地发现了我的路线,我可以验证对预期路线的请求是否正常工作 - 只有 Url.Action() 令人困惑。

有什么问题...?

尝试以下操作:

为您的路线命名:

[HttpGet, Route("{telemetryKey}/versions/latest", Name="LatestVersionInfoRoute")]
    public async Task<LatestVersionResponse> GetLatestVersionInfo(Guid telemetryKey)
    {
        // serious business logic
    }

使用Url.Link方法:

@Url.Link("LatestVersionInfoRoute", new { telemetryKey = Guid.Parse("43808405-afca-4abb-a92a-519489d62290") })

嗯,似乎有一些地方不对。

错误的帮手: 我应该使用 Url.HttpRouteUrl 从剃刀视图中生成 API link(Url.Link 用于从 API 控制器中生成 link)

与 aspnet-api-versioning 库冲突 出于某种原因(也许是一个错误?)我在控制器上的前缀(apiVersion 变量)破坏了 URL 辅助机制。

现在,我已经放弃了 aspnet-api-versioning 库,但在他们的 github 回购协议上创建了一个问题,以防它是一个错误。

因为我真的很讨厌创建和维护魔法字符串的想法,所以我采用了以下方法 - 每个控制器都有一个 public static class,其中包含路由名称的 const 值:

[RoutePrefix("api/v1/developers")]
public class DevelopersController : ApiController
{
    [HttpGet, Route("{developerId}/programs", Name = Routes.GetPrograms)]
    public async Task<IEnumerable<Program>> GetPrograms(Guid developerId){}

    public static class Routes
    {
        public const string GetPrograms = nameof(DevelopersController) +"."+ nameof(DevelopersController.GetPrograms);
    }
}

现在可以通过剃须刀控制器以简单且相对安全的方式使用它:

@Url.HttpRouteUrl(DevelopersController.Routes.GetPrograms, new { developerId = /* uniquest of guids */})

比魔术弦好一点。我还为控制器添加了一堆单元测试,我在其中验证每条路线都是唯一且正确的,并且路线 class 仅包含其包含的操作的路线。