#c# #asp.net-mvc #asp.net-core
Вопрос:
Если у меня есть контроллер с довольно большим количеством действий, выполняющий аналогичные действия, например, Получает данные из api, что-то делает с ним, возвращает представление с обновленной моделью. Есть ли лучший способ обработки ошибок? В настоящее время я делаю это с помощью ряда методов действий, очевидно, что это дублирование кажется неправильным, но я не могу придумать альтернативу. Спасибо
public async Task<IActionResult> method(string id)
{
var result = await _flightRepository.GetLightById(id);
if (!result.Valid)
{
return View("ErrorPage", result.Error.Message);
}
var viewModel = new FlightViewModel
{
Flight = result.Result
};
return View(viewModel);
}
В принципе, я хочу каким-то образом инкапсулировать логику обработки ошибок, чтобы возвращать представление об ошибке, если значение valid равно false, в противном случае заполнить viewmodel и вернуть представление. Свойство valid возвращает значение true, если с запросом произошла ошибка (это делается на уровне api).
Спасибо за любую помощь
Комментарии:
1. У вас есть базовый тип для ответа, возвращаемого
GetLightById
? Что-то вродеIReturn<T>
» а » или что-то похожее?2. метод расширения может хорошо подойти.
3. @A. Chiesa yeh возвращает репозиторий Result<T>, результат репозитория содержит модель T, а также свойство ошибки (с msg и httpstatuscode). Это заполняется в слое хранилища, если возникает проблема с получением данных
Ответ №1:
Вы должны использовать фильтры для повторного использования кода в своих действиях.
Например, в вашем случае ваш фильтр может быть:
public class FlightValidator: ActionFilterAttribute
{
private readonly string _flightIdRouteKey; // e.g 23
private readonly string _errorViewName; // e.g "ErrorPage"
private readonly IFlightRepository _flightRepo;
public FlightValidator(string flightIdRouteKey, string errorViewName, IFlightRepository flightRepository)
{
_flightIdRouteKey = flightIdRouteKey;
_errorViewName = errorViewName;
_flightRepo = flightRepository;
}
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
int flightId = (int)context.RouteData.Values[_flightIdRouteKey];
var result = await _flightRepo.GetFlightById(flightId);
if (!result.Valid)
{
context.Result = new ViewResult
{
ViewName = _errorViewName,
ViewData = new ViewDataDictionary(result.ViewData)
{
Model = model
}
};
return;
}
await next();
}
}
Теперь вы можете использовать фильтр следующим образом:
[TypeFilter(typeof(FlightValidator), Arguments = new object[] { "id", "ErrorPage"})]
public async Task<IActionResult> method(string id)
{
var viewModel = new FlightViewModel
{
Flight = result.Result
};
return View(viewModel);
}
Я предлагаю вам обратиться к документам Filters для получения дополнительной информации о фильтрах.
Комментарии:
1. Спасибо, я думаю, что и этот, и другой ответ являются жизнеспособными решениями, однако мне интересно, не является ли также излишним и более читабельным просто дублировать код… но СУХО …
2. DRY делает ваш код чистым , ремонтопригодным и масштабируемым, а также экономит много времени при повторном использовании кода в разных местах.
Ответ №2:
Если вы используете статический метод, подобный этому:
static async Task<IActionResult> ProcessAsync<T>(
Func<Task<RepositoryResult<T>>> process,
Func<T, IActionResult> ifValid
)
{
var result = await process();
if (!result.Valid)
{
return View("ErrorPage", result.Error.Message);
}
return ifValid(result.Result);
}
Тогда вы можете использовать его вот так:
public Task<IActionResult> method(string id)
{
return ProcessAsync(
() => _flightRepository.GetLightById(id),
flight =>
{
var viewModel = new FlightViewModel
{
Flight = flight
};
return View(viewModel);
}
);
}
Статический метод может быть определен в любом месте по вашему желанию — в базовом классе контроллера или в совершенно другом пространстве имен (в этом случае вы импортируете его с using static Some.Namespace.With.Class;
помощью .
Кроме того, я предлагаю вам использовать соглашение о Async
суффиксе для асинхронных методов — поэтому, поскольку GetLightById
возвращает Task<T>
, я предлагаю переименовать его GetLightByIdAsync
.
Комментарии:
1. Спасибо, я думаю, что и этот, и другой ответ являются жизнеспособными решениями, однако мне интересно, не является ли также излишним и более читабельным просто дублировать код… но СУХО …
2. Да, и, конечно, если вы когда-нибудь захотите добавить дополнительную стандартную проверку, вам тоже придется скопировать/вставить ее, поэтому обычно лучше запускать все с помощью одного метода (или фильтра, как в другом ответе).