问题
I'm creating a new webapi using attribute routing to create a nested route as so:
// 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);
}
I however want to return a route that isn't nested i.e.:
/api/Messages/{id}
which is defined on the messages controller. However The CreatedAtRoute call above is not resolving this route and instead throwing. Have I done something wrong, or does it not support routing to different api controller? n.b. the route I am trying to hit is not an attribute route, just a default one.
The exception is:
Message: "An error has occurred." ExceptionMessage: "UrlHelper.Link must not return null." ExceptionType: "System.InvalidOperationException" StackTrace: " at System.Web.Http.Results.CreatedAtRouteNegotiatedContentResult
1.Execute() at System.Web.Http.Results.CreatedAtRouteNegotiatedContentResult
1.ExecuteAsync(CancellationToken cancellationToken) at System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.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.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.GetResult() at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__0.MoveNext()"
If it doesn't support this, what is the canonical way to return a 201 and can I do it in a refactor safe way?
回答1:
Oh dear, this may be a new record for answering my own question.
return CreatedAtRoute("DefaultApi", new { controller = "messages", id = message.Id }, message);
does the trick. i.e. explicitly specifying the controller. I worked this our by seeing that the exception was related to the UrlHelper and reading its docs...
回答2:
Late to the party but an alternative answer. If the action you are routing to also uses attribute routing, you can give the route a name and pass that in to the CreatedAtRoute method. This is done by setting a Name
property on the Route
. Following your post example, consider the following action.
// GET: api/Messages/5
[Route("api/messages/{id}", Name="GetMessage")]
public async Task<IHttpActionResult> GetMessage(int id)
{
// get the message
}
Note that the Name
property on the route attribute, [Route("api/messages/{id}", Name="GetMessage")]
, is set to "GetMessage"
. By doing this we can call the CreatedAtRoute
method from the PostChannelMessage
action and pass in the route name like so:
return CreatedAtRoute("GetMessage", new { id = message.Id }, message);
This is a scenario I encountered and my searching led here so thought I would post this alternative answer in case it helps anyone else.
回答3:
Just adding to the answers above: on Attribute Routing:
I was caught out by the parameter name, took me an hour to realise that the parameter needs to named correctly otherwise the Url Helper will return null.
i.e if you have an action method like:
[Route("api/messages/{id}", Name="GetAction")]
public IHttpActionResult GetEntity(int mySpecialUniqueId)
{
// do some work.
}
Then the return should be:
return CreatedAtRoute("GetAction", new { mySpecialUniqueId = entity.Id }, entity);
On the more simple examples, the Id property kept throwing me off so i thought I would expand on it more in this answer to help save others time on this little issue.
See this more complicated example for more detail:
Attribute Routing and CreatedAtRoute
来源:https://stackoverflow.com/questions/23417559/createdatroute-routing-to-different-controller