ASP.NET Core "CreatedAtRoute" 失败

31

在我的ASP.NET Core应用程序中,我有一个控制器动作,如下所示:

[HttpPost]
public async Task<IActionResult> CreateSubscriber([FromBody] SubscriberDef subscriber)
{
    //...implementation removed

    var link = Url.Link("SubscriberLink", new { id = subscriber.ID });
        return Created(link, null);
}

上述代码按预期运行。但是,如果我使用内置方法“CreatedAtRoute”,那么就会出现异常:

[HttpPost]
public async Task<IActionResult> CreateSubscriber([FromBody] SubscriberDef subscriber)
{
    //...implementation removed

    return CreatedAtRoute("SubscriberLink", new { id = subscriber.ID });
}

异常信息如下:

System.InvalidOperationException: 找不到匹配的路由。

这个异常会导致服务返回500状态码。

两种情况下使用的是相同的路由,因此我不知道为什么第一个示例可以正常工作,而第二个示例不能。

我的 project.json 文件包含以下内容:

"frameworks": {
  "dnx46": { },
  "dnxcore50": { }
},

为了参考,命名路由由两部分组成。第一部分是控制器前缀:

[Route("api/[controller]")]
public class SubscribersController : Controller
{
    // ...
}

第二个是GET操作,其中实际的"SubscriberLink"路由被命名:

[HttpGet("{id}", Name = "SubscriberLink")]
[SwaggerResponse(HttpStatusCode.OK, Type = typeof(Subscriber))]
public async Task<IActionResult> GetSubscriber(Guid id)
{
    //...implementation removed...
    return Ok(subscriber);
}

你有什么想法?


1
SubscriberLink 路由名称定义在哪里? - haim770
@haim770 它在最后一个代码片段中被定义。 - Brent Arias
var link = Url.Link("SubscriberLink", new { id = subscriber.ID }); 返回正确的 URL 还是仅仅没有抛出异常? - haim770
3
这是一个研究充分且解释清晰的好问题的绝佳范例。干杯! - Nate Barbettini
1个回答

48

简短回答

你正在使用错误的 CreatedAtRoute 重载。使用接受三个参数的重载版本。

工作示例

例如,以下在我的机器上可以工作。

[Route("api/[controller]")]
public class SubscribersController : Controller
{
    public IActionResult Index()
    {
        var subscriber = new
        {
            Id = Guid.NewGuid(),
            FirstName = "Shaun",
            LastName = "Luttin"
        };

        // overload with three arguments
        return CreatedAtRoute(
            routeName: "SubscriberLink",
            routeValues: new { id = subscriber.Id },
            value: subscriber);
    }

    [HttpGet("{id}", Name = "SubscriberLink")]
    public IActionResult GetSubscriber(Guid id)
    {
        var subscriber = new
        {
            Id = id,
            FirstName = "Shaun",
            LastName = "Luttin"
        };

        return new JsonResult(subscriber);
    }
}

详情

结果是201响应。响应正文包含我们创建的实体(value参数)的详细信息,响应的Location标头包含指向该实体的URI。

响应包括我们创建的对象及其位置。

CreatedAtRoute有三个重载。

CreatedAtRoute(object routeValues, object value)
CreatedAtRoute(string routeName, object value)
CreatedAtRoute(string routeName, object routeValues, object value)

如果我们想传递路由名称和路由值,我们可以使用接受三个参数的重载方法。如果我们不想返回创建的实体的详细信息,我们可以将第三个参数传递为 null


应该是[Route("api/[controller]", Name = "SubscriberLink")],因为在您的情况下,您设置的是路由模板而不是名称。 - Fabien ESCOFFIER
1
这是不正确的。新的 HttpGetAttribute 确实 定义了一个 Name 属性,并且实现了提供路由名称的 IRouteTemplateProvider 接口。 - haim770
OP使用的是CreatedAtRoute(string routeName, object value),这似乎是正确的重载。 - haim770
结果证明并非如此,因为“value”未指定“id”路由值。相反,可以在路由定义中使“id”变为可选项,这样就会起作用。 - Shaun Luttin
4
哎呀!我认为第一和第二个重载都是误导性的和被曲解的。每当提供一个 Location 标头时,肯定需要同时提供相关的路由和 routeValues。相比之下,从不需要为回复提供内容。所以,在我看来,那些重载都很混乱。简而言之,目前只有第三个重载有任何价值。感谢您提醒我! - Brent Arias
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接