Reputation: 6363
I've read a few SO posts and none of them quite cover my scenario so I'm going to post here.
Given the following route config registration:
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
and these controller actions in a controller that inherits from ApiController:
public GetDocumentsResponse Post([FromBody]GetDocumentsRequest request)
{
}
public FinishDocumentsResponse Post([FromBody] FinishDocumentsRequest request)
{
}
public class GetDocumentsRequest
{
public string CorrelationId { get; set; }
public int Id { get; set; }
public string ObjectId { get; set; }
public string BusinessArea { get; set; }
public string UserId { get; set; }
public string SystemName { get; set; }
public string SystemToken { get; set; }
public Letter LetterDetails { get; set; }
public List<KeyValuePair<string, string>> KeyValue { get; set; }
}
public class FinishDocumentsRequest
{
public string CorrelationId { get; set; }
public string[] Documents { get; set; }
}
I thought doing it this way would be enough disambiguation for the IHttpActionSelector to correctly choose the route, but unfortunately it is not.
So my questions is "Is there a way to make this code work correctly, and keep it in the same controller?"
Thank you, Stephen
Upvotes: 1
Views: 627
Reputation: 2594
The request routing pipeline isn't smart enough to determine if the body of the request matches the parameter type (aka overloading). (The compiler is smart enough, which is why this compiles and you have runtime issues.)
You have a couple of different options.
[Route(<ActionName>)]
attribute on both of your posts.GetDocuments
and one for FinishDocuments
Post
method that is ambiguous. (I'd avoid this)If you choose option 1, your API uri will have to be .../api/MyController/MyActionName
rather than .../api/MyController/
. It's also advisable to add [HttpGet]
and [HttpPost]
attributes on your methods.
Sample:
public class DocumentController : ApiController
{
// POST /api/Document/GetDocuments
[HttpPost]
[Route("GetDocuments")]
public GetDocumentsResponse Post([FromBody]GetDocumentsRequest request) { ... }
// POST /api/Document/FinishDocuments
[HttpPost]
[Route("FinishDocuments")]
public FinishDocumentsResponse Post([FromBody] FinishDocumentsRequest request){ ...}
}
If you choose option 2, you have to maintain an additional code file.
public class GetDocumentsController : ApiController
{
// POST /api/GetDocuments
[HttpPost]
public GetDocumentsResponse Post([FromBody]GetDocumentsRequest request) { ... }
}
public class FinishDocumentsController : ApiController
{
// POST /api/FinishDocuments/
[HttpPost]
public FinishDocumentsResponse Post([FromBody] FinishDocumentsRequest request){ ...}
}
If you choose option 3, may God have mercy on your soul you're going to have a bad time maintaining it.
Upvotes: 1
Reputation: 16846
You could use attribute routing for this.
Define the route as a string in the Route attribute ontop of the methods as this
[Route("api/controller/Post1")]
[HttpPost]
public GetDocumentsResponse Post([FromBody]GetDocumentsRequest request)
{
}
[Route("api/controller/Post2")]
[HttpPost]
public FinishDocumentsResponse Post([FromBody] FinishDocumentsRequest request)
{
}
Upvotes: 1
Reputation: 15968
Add the Route attribute decoration to your web api functions and that will assit the selector to choose the route:
[Route("Post1")]
public GetDocumentsResponse Post([FromBody]GetDocumentsRequest request)
{
}
[Route("Post2")]
public FinishDocumentsResponse Post([FromBody] FinishDocumentsRequest request)
{
}
I also recommend adding the http method decoration such as [HttpPost]
or [HttpGet]
Upvotes: 0