Enable IActionResult Response
pdevito3 opened this issue · comments
Research For Duplicates
I looked through the issues and docs and didn't see anything discussing this. Apologies if it's there and I missed it.
Describe the feature you'd like
So I love the idea of breaking my controller out like this and started updating my api template to use API Endpoints. Once I started though, I noticed that, as far as I can tell, I have to chose an explicit response for my endpoints that previously use IActionResult
as described in the MS docs. This seems to be due to the WithResponse
method wrapping whatever is passed to it in a Task<ActionResult<>>
. This is obviously by design and I'm sure I can get things to at least working as is, but it seems like we should have the option here to do things, as far as I know, semantically correct.
Example
Here's an example for a POST.
[Route("/api/patients")]
public class PostPatientEndpoint : BaseAsyncEndpoint
.WithRequest<PatientForCreationDto>
.WithResponse<IActionResult>
{
private readonly IMediator _mediator;
public PostPatientEndpoint(IMediator mediator)
{
_mediator = mediator;
}
/// <response code="201">Patient created.</response>
/// <response code="400">Patient has missing/invalid values.</response>
/// <response code="409">A record already exists with this primary key.</response>
/// <response code="500">There was an error on the server while creating the Patient.</response>
[ProducesResponseType(typeof(Response<PatientDto>), 201)]
[ProducesResponseType(typeof(Response<>), 400)]
[ProducesResponseType(typeof(Response<>), 409)]
[ProducesResponseType(500)]
[Consumes("application/json")]
[Produces("application/json")]
[SwaggerOperation(
Summary = "Creates a new Patient record.",
OperationId = "PostPatientEndpoint",
Tags = new[] { "Patients" })
]
[HttpPost, MapToApiVersion("1.0")]
public override async Task<IActionResult> HandleAsync([FromBody] PatientForCreationDto patientForCreation,
CancellationToken cancellationToken = new CancellationToken())
{
// add error handling
var command = new AddPatient.AddPatientCommand(patientForCreation);
var commandResponse = await _mediator.Send(command);
var response = new Response<PatientDto>(commandResponse);
return CreatedAtRoute("GetPatient",
new { commandResponse.PatientId },
response);
}
}
You can use WithoutResponse
for this (misleading name in this case, but it will work).
This will create a Task<ActionResult>
method that is able to return CreatedAtRoute
.
PS: I think, WithoutResponse
should really be named WithActionResponse
(or WithActionResult
), and WithoutResponse
should result in Task
method (without type argument).
Haven't tried this yet, but even if that does work, how do you resolve the route argument to point to the get route for getting the data from the POST (i.e. GetPatient
)?
Didn't quite understand you there, but
return CreatedAtRoute("GetPatient",
new { commandResponse.PatientId },
response);
will work, if that's what you asking.
Didn't quite understand you there, but
return CreatedAtRoute("GetPatient", new { commandResponse.PatientId }, response);will work, if that's what you asking.
This was more of a follow up.
I don't have a chance to try it right now, but last I remember it didn't work because it doesn't know where GetPatient
is and I still don't see how it could know, unless ApiEndpoint has some key mapping under the hood across all routes. The only way this currently works to my knowledge is if is this POST is in the same controller as GetPatient
Can you show how GetPatient
controller/action looks? More specifically, what is the route to it and what arguments it accepts.
This is supported in 4.x release, I believe. We now have native ActionResult and ActionResult options with the fluent generic base class, as well as ability to just use the base class directly with whatever signature you want.