我正在使用属性路由创建一个新的webapi来创建嵌套路由,如下所示:
// PUT: api/Channels/5/Messages
[ResponseType(typeof(void))]
[Route("api/channels/{id}/messages")]
public async Task<IHttpActionResult> PostChannelMessage(int id, Message message)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (id != message.ChannelId)
{
return BadRequest();
}
db.Messages.Add(message);
await db.SaveChangesAsync();
return CreatedAtRoute("DefaultApi", new { id = message.Id }, message);
}
但是我希望返回一个没有嵌套的路由,即:
/api/Messages/{id}
在消息控制器上定义。但是,上面的CreatedAtRoute调用不解析此路线而是抛出。我做错了什么,或者它不支持路由到不同的api控制器?注:我试图点击的路线不是属性路线,只是默认路线。
例外是:
消息:“发生了错误。” ExceptionMessage:“UrlHelper.Link不能返回null。” ExceptionType:“System.InvalidOperationException” StackTrace:“在System.Web.Http.Results.CreatedAtRouteNegotiatedContentResult
1.Execute() at System.Web.Http.Results.CreatedAtRouteNegotiatedContentResult
1.SestcuteAsync(CancellationToken cancellationToken)在System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()---来自之前的堆栈跟踪结束抛出异常的位置---在System.Runtime.Compiler服务.TaskAwaiter1.GetResult() at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter
1的System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)的System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)中.GetResult()at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__0.MoveNext()“
如果它不支持这个,那么返回201的规范方法是什么?我可以以重构安全的方式进行吗?
答案 0 :(得分:50)
return CreatedAtRoute("DefaultApi", new { controller = "messages", id = message.Id }, message);
诀窍。即明确指定控制器。我通过看到异常与UrlHelper相关并阅读其文档来解决这个问题......
答案 1 :(得分:33)
晚会,但另一个答案。如果要路由到的操作也使用属性路由,则可以为路由指定名称并将其传递给CreatedAtRoute方法。这是通过在Name
上设置Route
属性来完成的。按照您的帖子示例,请考虑以下操作。
// GET: api/Messages/5
[Route("api/messages/{id}", Name="GetMessage")]
public async Task<IHttpActionResult> GetMessage(int id)
{
// get the message
}
请注意,路由属性Name
上的[Route("api/messages/{id}", Name="GetMessage")]
属性设置为"GetMessage"
。通过执行此操作,我们可以从CreatedAtRoute
操作调用PostChannelMessage
方法并传递路由名称,如下所示:
return CreatedAtRoute("GetMessage", new { id = message.Id }, message);
这是我遇到的一个场景,我的搜索引导到这里,以为我会发布这个替代答案,以防它帮助其他人。
答案 2 :(得分:2)
只需添加上面的答案: on Attribute Routing:
我被参数名称抓住了,花了我一个小时才意识到参数需要正确命名,否则Url Helper将返回null。
即如果你有一个行动方法,如:
[Route("api/messages/{id}", Name="GetAction")]
public IHttpActionResult GetEntity(int mySpecialUniqueId)
{
// do some work.
}
然后返回应该是:
return CreatedAtRoute("GetAction", new { mySpecialUniqueId = entity.Id }, entity);
在更简单的例子中,Id属性一直让我失望所以我认为我会在这个答案中更多地扩展它以帮助节省其他人在这个小问题上的时间。
有关详细信息,请参阅此更复杂的示例:
答案 3 :(得分:0)
对于 dotnet 5,解决方案略有不同:
带有 GET 方法的控制器应该如下所示:
[Route("api/[controller]")]
[ApiController]
public class ItemController : ControllerBase
{
[HttpGet("{id}", Name = nameof(Get))]
public ActionResult Get([FromRoute] int id)
{
// code here.
}
}
还有像这样的 CreatedAtAction
方法:
return CreatedAtAction(nameof(ItemController.Get), "Item", new { item.Id }, item);
可以将到控制器的路由直接指定为 CreatedAtAction
方法的第二个参数。