Как стать автором
Обновить

Комментарии 12

Основанный на фреймворке ASP.NET MVC, Web API был предназначен для того, чтобы рассматривать глаголы и существительные архитектуры REST как граждан первого класса.

Звучит сильно!

Но значит ли это, что он плох?

По крайней мере вот это:

catch (Exception)
{
   return Results.BadRequest("Failed");
}

Это не только плохо, но и совсем не нужно. Во первых, по коду ясно, что тут должен быть 500, во вторых эту 500 ASP.NET pipeline сам вернет, в третьих он её еще и сам запишет в лог. А в четвертых тут совсем нет нужды возвращать IActionResult, можно просто коллекцию объектов и в итоге весь этот метод превращается просто в:

    [HttpGet]
    public IEnumerable<OrderViewModel> Get(bool includeItems = true) =>
      _mapper.Map<IEnumerable<OrderViewModel>>(
      	_repository.GetOrdersByUser(User.Identity.Name, includeItems));

А для обработки типа:

if (client == null)
{
    return Results.NotFound();
}

лично я уже давным-давно использую однажды написанный готовый кастомный атрибут (MVC action filter в котором от силы пару дюжин строчек кода), и выглядит это, например, так:

[HttpGet(clients/{id}]
[NullMeans404] // <== преобразует возвращенный ObjectResult с null в NotFoundResult
public async Client GetClient([FromRoute] int id) => await _repo.GetClientAsync(id);

Про содержание статьи в целом еще можно поспорить (мне, например, еще в принципе не нравится что логика обработки запросов, какая бы она простая не была, у вас смешивается с кодом конфигурации приложения), но примеры кода, на мой взгляд, однозначно плохи.

Про NullMeans404 - отличная идея. Не хотите выложить исходники в открытый доступ и опубликовать в виде нугета?

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

Второе. Если настройка эндпойнтов уезжает в startup/program, это плохо. Файлы получают больше ответственности. Сложнее делить логически (как это можно делать контроллерами). Вот если бы можно было прямо в файле контроллера описывать эндпойнты в таком стиле, как много лет назад в язык пришли get; set; без необходимости расписывать аксессоры и поле данных - другой вопрос.

Третье. В некоторых типах автотестов мне удобно вызывать методы контроллеров, чтобы покрыть их, почти как реальные запросы, без моков и фейков. А тут как? Вызывать из тестов сервисные методы? А если я хочу прям всю цепочку от начала покрыть?

Дальше лень перечислять. Одни минусы какие-то.

Don’t JS my C#

В статье явно написано, что это хорошо пойдет для прототипирования или простенького приложения. И практики еще не наработали.

Ну ок. Мне просто сложно рассматривать какой-то аспект языка как «только для прототипов», которые имеют свойство перерастать в постоянное решение, иногда «срочно, надо ещё вчера, переделывать нет времени», поэтому выбираю те подходы, которые можно продолжать без переписывания уже созданного.

1. Всегда можно вынести всё лишнее из main.cs в методы-расширения (мы так делаем даже с обычными контроллерами. main.cs получается строк пять от силы)

Или делать методы однострочными — тогда это будет по сути описание всех маршрутов, что вполне удобно (не нужно по всему проекту судорожно атрибуты читать и пытаться понять, какой будет итоговый маршрут)

2. Удобство: можно легко на этапе стартапа включать/выключать некоторые ендпоинты простым ифом. Или добавлять новые через циклы (хз где это может пригодиться, а вот выключение — вполне)

3.
Вот если бы можно было прямо в файле контроллера описывать эндпойнты в таком стиле, как много лет назад в язык пришли get; set; без необходимости расписывать аксессоры и поле данных — другой вопрос.

Велком: Carter

4.
В некоторых типах автотестов мне удобно вызывать методы контроллеров, чтобы покрыть их, почти как реальные запросы, без моков и фейков.

Ценность таких автотестов почти нулевая, так как игнорируется весь конвеер обработки запросов. Если хочется так протестировать — выноси логику из контроллеров в нормальные сервисы или Mediatr, и тестируй уже их. Контроллеры же лучше воспринимать как такое объектное описание маршрутов.
А если я хочу прям всю цепочку от начала покрыть?

Как и с контроллерами — поднимать сервер и слать http запросы.
Если прям как вы описываете — делаешь хендлеры не анонимными и вызываешь их. Точно также, как и контроллеры. (но есть нюанс)

А если и правда мелкий api на пару методов и ты не хочешь пихать в входящие параметры DI сервис, как правильно стоит это в коде изложить? Обернуть все методы в scope ?

var bldr = WebApplication.CreateBuilder(args);
bldr.Services.AddDbContext<JurisContext>();

var app = bldr.Build();

using (var scope = bldr.Services.CreateScope()){

var myDb = scope.Get<JurisContext>();

app.MapGet("/", () => mydb.GetById(1));

app.MapGet("/page2", () => mydb.GetById(1));

app.MapPost("/page2", (inputModel) => mydb.GetById(1));

}

Выглядит не очень

Оно и работать не будет.

Скоуп будет задиспозен сразу после выхода из using, вместе со всеми сервисами

ну перед скобкой поставить app.Run();

Но зачем? Ведь скоуп предполагается соответствующим какому-то действию, а в контектсе ASP.NET веб приложения - обработке входящего запроса. Тем более, в статье показано, что можно инжектить зависимости в лямбду-обработчик.

Если использовать DbContext как синглтон (долгоживущий класс), то уже через пару сотен запросов на достать из БД/сохранить в БД Вы увидите деградацию производительности, а через пару тысяч запросов - то что Ваш сервис перестал работать.

Щачем тогда вообще скоуп тормошить, если, по сути, у тебя один инстанс на все приложение получается. Возми та сделай статик класс. Только это будет все равно говнецо

Зарегистрируйтесь на Хабре, чтобы оставить комментарий