Что служба должна возвращать контроллеру в операциях CRUD?

Рассмотрим контроллер ASP.NET для картофеля. Он имеет конечную точку для операции удаления.

[HttpDelete("{potatoId:long}")]
public async Task<IActionResult> DeletePotato([FromRoute] long potatoId, CancellationToken ct)
{
    var result = _potatoService.Delete(potatoId, ct);
    return Ok();
}

Он использует _potatoService для выполнения операции удаления. Теперь могут произойти две вещи:

  1. Картошки не существует (и мы должны вернуть 404)
  2. Potato успешно удален (и мы должны вернуть 204)

Мой вопрос заключается в том, какой ответ должен возвращать _potatoService, чтобы контроллер мог определить результат операции.

Объект StatusCode? Но эта служба может быть запрошена другими частями системы, которые не знают об объектах Microsoft.AspNetCore.Mvc.

Целое число кода состояния? Но опять же другим частям системы пришлось бы полагаться на этот целочисленный ответ, чтобы узнать результат.

Пользовательский объект/перечисление? Если да, то я не уверен, какова наилучшая практика.

Объект кода состояния, сгенерированный Ok(), NotFound() и другими вспомогательными методами. Контроллеры напрямую не вызываются другими частями системы. Если другим частям нужна логика, она должна быть частью какой-то службы, которая затем используется контроллером и другими частями системы. Методы службы могут возвращать модель или что-то еще, что, в свою очередь, преобразуется контроллером в объект состояния. В идеале действия контроллера должны быть лишь тонкими оболочками между бизнес-объектами и объектами запроса/ответа.

ckuri 02.11.2022 18:47

Оберните try/catch на своем контроллере, и если возникнет исключение, пусть оно всплывет и обработает его соответствующим образом. В противном случае вы вернетесь в порядке.

GH DevOps 02.11.2022 19:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
94
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Один из подходов состоит в том, чтобы сделать идемпотентными как метод службы, так и метод контроллера. В данном случае это означает, что «удаление» объекта возвращает один и тот же результат независимо от того, существует ли объект.

Во-первых, что возвращает сервис картошки? Не нужно ничего возвращать. Предполагая, что это асинхронно, это может быть

async Task Delete(potatoId, ct);

Возможны два исхода.

  1. Метод не генерирует исключение. Либо элемент существовал и был удален, либо его не существовало. Результат одинаков в любом случае.
  2. Метод выдает исключение.

Что возвращает контроллер? Он может вернуть 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
}

Другие вопросы по теме