Consider an ASP.NET controller for potatoes. It has an endpoint for Delete operation.
[HttpDelete("{potatoId:long}")]
public async Task<IActionResult> DeletePotato([FromRoute] long potatoId, CancellationToken ct)
{
var result = _potatoService.Delete(potatoId, ct);
return Ok();
}
It uses _potatoService to perform Delete operation. Now two things can happen:
- The potato does not exist (and we should return 404)
- Potato is deleted successfully (and we should return 204)
My question is what kind of response should _potatoService return in order for controller to determine result of the operation.
A StatusCode object? But this service could be requested by other parts of the system which are not aware of Microsoft.AspNetCore.Mvc objects.
A status code integer? But then again other parts of the system would have to rely on this integer response in order to find out the result
A custom object / enum? If so I am not sure what the best practice is.
CodePudding user response:
One approach is to make both the service method and the controller method idempotent. In this case that means that "deleting" an entity returns the same result whether or not the entity exists.
First, what does the potato service return? It doesn't need to return anything. Assuming it's asynchronous, it could be
async Task Delete(potatoId, ct);
There are two possible outcomes.
- The method does not throw an exception. Either the item existed and was deleted, or it didn't exist. The result is the same either way.
- The method throws an exception.
What does the controller return? It could return 204 if it's successful or a 500 if the delete fails.
Think of it like doing an update. Suppose you were updating the potato's name to "Awesome potato." If it's successful you return a 200 or 204.
But what if the potato already had that name? Would you return an error? No, you'd return the same result because whether the name changes or not, the outcome is the same. It now has that name.
That's a short version of what idempotency does. Duplicate requests don't cause errors unnecessarily. In the above cases there's no reason to return an error. If the user tried to update a potato that doesn't exist or create a duplicate potato when it's not allowed, those would be reasons to return errors.