Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CreatedAtRoute routing to different controller

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.CreatedAtRouteNegotiatedContentResult1.Execute() at System.Web.Http.Results.CreatedAtRouteNegotiatedContentResult1.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.TaskAwaiter1.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?

like image 212
DanH Avatar asked May 01 '14 22:05

DanH


2 Answers

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...

like image 175
DanH Avatar answered Oct 20 '22 00:10

DanH


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.

like image 28
ceej Avatar answered Oct 20 '22 01:10

ceej