Рассмотрим контроллер ASP.NET для картофеля. Он имеет конечную точку для операции удаления.
[HttpDelete("{potatoId:long}")]
public async Task<IActionResult> DeletePotato([FromRoute] long potatoId, CancellationToken ct)
{
var result = _potatoService.Delete(potatoId, ct);
return Ok();
}
Он использует _potatoService для выполнения операции удаления. Теперь могут произойти две вещи:
Мой вопрос заключается в том, какой ответ должен возвращать _potatoService, чтобы контроллер мог определить результат операции.
Объект StatusCode? Но эта служба может быть запрошена другими частями системы, которые не знают об объектах Microsoft.AspNetCore.Mvc.
Целое число кода состояния? Но опять же другим частям системы пришлось бы полагаться на этот целочисленный ответ, чтобы узнать результат.
Пользовательский объект/перечисление? Если да, то я не уверен, какова наилучшая практика.
Оберните try/catch на своем контроллере, и если возникнет исключение, пусть оно всплывет и обработает его соответствующим образом. В противном случае вы вернетесь в порядке.





Один из подходов состоит в том, чтобы сделать идемпотентными как метод службы, так и метод контроллера. В данном случае это означает, что «удаление» объекта возвращает один и тот же результат независимо от того, существует ли объект.
Во-первых, что возвращает сервис картошки? Не нужно ничего возвращать. Предполагая, что это асинхронно, это может быть
async Task Delete(potatoId, ct);
Возможны два исхода.
Что возвращает контроллер? Он может вернуть 204 в случае успеха или 500 в случае сбоя удаления.
Думайте об этом как об обновлении. Предположим, вы меняли название картошки на «Отличная картошка». В случае успеха вы возвращаете 200 или 204.
Но что, если у картофеля уже было это имя? Вы бы вернули ошибку? Нет, вы бы вернули один и тот же результат, потому что независимо от того, меняется ли имя или нет, результат остается тем же. Теперь у него такое название.
Это краткая версия того, что делает идемпотентность. Повторяющиеся запросы не вызывают ошибок без необходимости. В приведенных выше случаях нет причин возвращать ошибку. Если пользователь попытается обновить несуществующую картофелину или создать дубликат картофелины, когда это не разрешено, это будет причиной возврата ошибок.
Вам не нужно ничего возвращать из вашей службы в ваш контроллер, но для обработки различных сценариев с ошибками вы можете настроить свой контроллер с помощью блока try/catch и создать собственные исключения для различных сценариев в вашей службе. В этом случае у вас будет ответ 200 для успешных операций и ответ 400 с пользовательскими сообщениями для неудачных операций. Вот пример:
[HttpDelete("{potatoId:long}")]
public async Task<IActionResult> DeletePotato([FromRoute] long potatoId, CancellationToken ct)
{
try
{
var result = _potatoService.Delete(PotatoId, ct);
return Ok();
{
catch(Exception ex)
{
return BadRequest(ex.Message);
}
}
В вашем сервисе (также может быть асинхронным):
public void Delete (potatoId, ct)
{
// check if record exists
if (recordExists)
{
//proceed to delete
}
else
{
throw new Exception("Record does not exist");
}
}
вы можете вернуться в соответствии с вашим результатом. но все же вы хотите вернуться как ОК, только тогда вы можете создать общую модель ответа с полями ниже
Public class ResponseModel
{
public dynamic Result { get;set;} // to return any dataset optional
public bool Status { get;set;} // based on delete operation
public string Message { get;set;} // Return your reason may of exception
}
Объект кода состояния, сгенерированный
Ok(),NotFound()и другими вспомогательными методами. Контроллеры напрямую не вызываются другими частями системы. Если другим частям нужна логика, она должна быть частью какой-то службы, которая затем используется контроллером и другими частями системы. Методы службы могут возвращать модель или что-то еще, что, в свою очередь, преобразуется контроллером в объект состояния. В идеале действия контроллера должны быть лишь тонкими оболочками между бизнес-объектами и объектами запроса/ответа.