Вы разрабатывали веб-приложение с нуля на ASP.NET Core?

Ответ

Да, неоднократно. Типичный стек для нового проекта включает:

  • Backend: ASP.NET Core Web API для RESTful-сервисов или MVC для серверного рендеринга.
  • Аутентификация/Авторизация: JWT-токены или Identity Server.
  • Доступ к данным: Entity Framework Core (Code-First или Database-First) с репозиториями и Unit of Work.
  • Документация API: Интеграция Swagger (Swashbuckle) с XML-комментариями.
  • Фронтенд: Для SPA — отдельное приложение на React/Angular/Vue, взаимодействующее с API. Для более простых случаев — Razor Pages или Blazor Server/WASM.
  • Инфраструктура: Docker-контейнеризация, развертывание в облаке (Azure/AWS), настройка CI/CD пайплайнов (GitHub Actions, GitLab CI).

Пример контроллера для Web API:

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;

    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }

    [HttpGet]
    [ProducesResponseType(typeof(IEnumerable<ProductDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetAll()
    {
        var products = await _productService.GetAllAsync();
        return Ok(products);
    }

    [HttpPost]
    [ProducesResponseType(typeof(ProductDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> Create([FromBody] CreateProductRequest request)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        var createdProduct = await _productService.CreateAsync(request);
        return CreatedAtAction(nameof(GetById), new { id = createdProduct.Id }, createdProduct);
    }
}

Ключевые практики: разделение ответственности (Controllers -> Services -> Repositories), использование DTO, валидация запросов, корректные HTTP-статусы и обработка ошибок глобально через middleware.

Ответ 18+ 🔞

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

Смотри, обычно это выглядит как какой-то овердохуища стандартный набор:

  • Бэкенд: Ну, ASP.NET Core Web API, если делаем эти ваши REST-сервисы. А если проект — какая-нибудь древняя контора, где всё должно быть просто и сразу, то MVC для серверного рендеринга, чтобы не заморачиваться.
  • Вход-выход: JWT-токены, чтобы не хранить сессии, либо Identity Server, если всё совсем серьёзно и хочется поумничать.
  • База данных: Entity Framework Core, само собой. Code-First, если мы боги и проектируем с нуля, или Database-First, если нам досталась какая-то ебанько-наследие от предков. И конечно, репозитории с Unit of Work, чтобы все архитектурные гуру в команде не сосали с нас.
  • Документация для API: Swagger (Swashbuckle), куда же без него. Только не забудь XML-комментарии навесить, а то будет пустая страница, и фронтендеры тебе мозг вынесут.
  • Фронтенд: Если делаем модное SPA — тогда отдельное приложение на React, Angular или Vue, которое будет дергать наш API. Если проект попроще — Razor Pages или Blazor, чтобы не бегать между двумя студиями.
  • Всякая инфраструктурная хуйня: Docker, чтобы упаковать всё это добро, облако (Azure обычно), и CI/CD пайплайны (GitHub Actions, GitLab CI), чтобы автоматически деплоить после каждого коммита и ломать продакшн в три часа ночи.

Вот, смотри, как обычно выглядит контроллер, чтоб ты понимал масштаб трагедии:

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;

    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }

    [HttpGet]
    [ProducesResponseType(typeof(IEnumerable<ProductDto>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetAll()
    {
        var products = await _productService.GetAllAsync();
        return Ok(products);
    }

    [HttpPost]
    [ProducesResponseType(typeof(ProductDto), StatusCodes.Status201Created)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> Create([FromBody] CreateProductRequest request)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        var createdProduct = await _productService.CreateAsync(request);
        return CreatedAtAction(nameof(GetById), new { id = createdProduct.Id }, createdProduct);
    }
}

А главное, что все делают вид, что следуют каким-то высшим практикам: разделение ответственности (контроллеры -> сервисы -> репозитории), использование DTO, валидация, правильные HTTP-статусы. И конечно, глобальная обработка ошибок через middleware, чтобы не засирать каждый метод одинаковыми try-catch блоками. Выглядит умно, а по факту — стандартная обвязка, без которой сейчас ни один проект не обходится, ёпта.