I have a controller with the following RouteAttribute:
[Route("api/[controller]/{customerId}")]
Now I have the following action method with the route:
[HttpGet("CustomerDetails")]
public async Task<ActionResult<GetCustomerDetailsQueryResponse>> GetCustomerDetail(Guid customerId)
Now, I would like to re-purpose this endpoint such that when I provide customer number, I should be able to get customer details based on that.
So if I make a postman call to: /api/Customers/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/CustomerDetails, I would get customer details based on the Guid Id.
Now the same endpoint should work if I make the call /api/Customers/01234/CustomerDetails.
What I have tried: I have tried the following:
[HttpGet("{customerNumber:int?}/CustomerDetails")]
public async Task<ActionResult<GetCustomerDetailsQueryResponse>> GetCustomerDetail(Guid customerId, int customerNumber)
But unfortunately, this doesn't work. Any ideas on making this work?
CodePudding user response:
You want three methods:
- Accept a guid, no routing attribute
- Accept an int, no routing attribute
- Accept a string, always route here as a string
The return types of all three method stay the same.
Method #3 will try to parse a guid, and then call method #1 if it succeeds, returning it's result. If it can't parse the guid it will try to parse an int and call method #2 if it succeeds, returning it's result. If that also fails, send a 404.
One additional step you can take is having one of the first two methods only do enough work to transform it's argument to the other method's key type, and then call that method. In this way, you only have one implementation which actually builds the response, or only one query to maintain. However, this does have a performance cost for that route, and so you want to be careful which you choose. It might even matter enough to be worth keeping both sets of code.
[ApiController]
[Route("[controller]")]
public class CustomersController : ControllerBase
{
[HttpGet("{customerId:string}/CustomerDetails")]
public async Task<ActionResult<GetCustomerDetailsQueryResponse>> GetCustomerDetails(string customerId)
{ // could probably shorten this via pattern matching, but that's not part of my workflow yet
Guid cIdGuid;
int cIdInt;
if (Guid.TryParse(customerId, out cId))
{
return CustomerDetailsGuidHelper(cIdGuid);
}
else if (int.TryParse(customerId, out cIdInt))
{
return CustomerDetailsGuidHelper(cIdInt);
}
else
{
return My404Result();
}
}
private async Task<ActionResult<GetCustomerDetailsQueryResponse>> CustomerDetailsGuidHelper(Guid customerId)
{
int customerIntId = await DB.GetIntIdViaGuid(customerID);
return CustomerDetailsIntHelper(customerIntId);
}
private async Task<ActionResult<GetCustomerDetailsQueryResponse>> CustomerDetailsIntHelper(int customerId)
{
// build your CustomerDetails json here
}
}
CodePudding user response:
minimal example
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
public TestController(){}
[HttpGet("{guid:guid}/details")]
public System.Guid GetIdGuid(System.Guid guid)
{
return guid;
}
[HttpGet("{id:int}/details")]
public int GetIdInt(int id)
{
return id;
}
}
