Практические задания

Исходный файл: Практические задания.docx
Лабораторная работа 1: Базовое приложение ASP.NET Core MVC
Пункт 1. Создание проекта ASP.NET Core MVC
Первым шагом является создание нового проекта ASP.NET Core MVC с использованием шаблона, предоставляемого Visual Studio или .NET CLI. В процессе инициализации важно выбрать опцию MVC, чтобы фреймворк автоматически сгенерировал базовую структуру проекта, включая папки Controllers, Views и Models. На этом этапе также формируется файл Program.cs, который служит точкой входа и отвечает за настройку веб-хоста и middleware-конвейера. Создание проекта закладывает основу для разделения обязанностей между компонентами (модель, представление, контроллер), что соответствует философии MVC, описанной в лекции 1.
Пункт 2. Настройка DI для сервисов в Program.cs
В Program.cs регистрируются сервисы, необходимые для работы приложения. Например, для интернет-магазина можно добавить сервис работы с товарами (IProductService) и его реализацию (ProductService), используя метод AddScoped для обеспечения одного экземпляра на запрос. Также настраивается контекст базы данных через AddDbContext, если используется Entity Framework Core. DI-контейнер автоматически внедряет зависимости в контроллеры через конструкторы, что способствует слабой связанности и тестируемости. Этот этап основывается на принципах dependency injection, рассмотренных в лекции 1.
Пункт 3. Реализация контроллера с методами GET и POST
Создайте контроллер ProductController, который будет обрабатывать запросы, связанные с товарами. Добавьте действие Index (GET) для отображения списка товаров, используя сервис IProductService. Для обработки формы добавления товара реализуйте действие Create (POST), принимающее модель Product. На этом этапе важно соблюдать соглашения MVC: методы GET используются для получения данных, POST — для их модификации. Логика контроллеров и их связь с DI подробно описаны в лекции 1.
Пункт 4. Создание модели данных с атрибутами DataAnnotations
Определите класс Product с свойствами Id, Name, Price и Description. Примените атрибуты валидации из пространства имен DataAnnotations: [Required] для Name, [Range(1, 1000)] для Price. Эти атрибуты не только задают правила валидации, но и влияют на отображение полей в формах (например, через HTML-хелперы). Модель данных служит основой для Model Binding и валидации, что подробно разобрано в лекции 2.
Пункт 5. Настройка Model Binding для параметров маршрута
Настройте маршрутизацию так, чтобы идентификатор товара передавался через URL (например, /product/details/5). Для этого используйте атрибут [Route("product/details/{id}")] над действием Details в контроллере. Model Binding автоматически преобразует значение id из маршрута в параметр метода типа int. Приоритет источников данных (маршрут, query string, тело запроса) объясняется в лекции 2.
Пункт 6. Реализация Model Binding для данных формы
В действии Create (POST) контроллера ProductController параметр типа Product автоматически привязывается к данным, отправленным через форму. Например, если форма содержит поля Name и Price, фреймворк заполнит соответствующие свойства модели. Для явного указания источника данных можно использовать атрибуты вроде [FromForm], хотя в большинстве случаев это не требуется. Принципы работы FormValueProvider и других поставщиков значений описаны в лекции 2.
Пункт 7. Валидация модели через атрибуты [Required], [Range]
После привязки данных модели проверьте её валидность через ModelState.IsValid в действии Create. Если валидация не пройдена (например, Price = 0), действие вернет представление с формой, отобразив сообщения об ошибках. Атрибуты [Required] и [Range] активируют серверную валидацию, что критично для защиты от некорректных данных. Подробности о валидации через DataAnnotations приведены в лекции 2.
Пункт 8. Создание кастомного Model Binder для строки запроса
Реализуйте кастомный биндер для преобразования строки запроса в объект. Например, если URL содержит параметры sortBy=price&order=asc, создайте класс SortOptions и биндер, который маппит эти параметры в его свойства. Зарегистрируйте биндер через services.AddControllers(options => options.ModelBinderProviders.Insert(0, new SortOptionsBinderProvider())) в Program.cs. Примеры создания кастомных биндеров приведены в лекции 2.
Пункт 9. Интеграция валидации в действия контроллера
В каждом POST-действии контроллера добавьте проверку ModelState.IsValid. Если модель невалидна, возвращайте представление с формой, передавая модель обратно для отображения ошибок. Это обеспечивает корректную обработку пользовательского ввода и предотвращает сохранение некорректных данных. Взаимодействие Model Binding и валидации описано в лекции 2.
Пункт 10. Обработка ошибок валидации в представлениях
В Razor-представлениях используйте HTML-хелперы типа ValidationMessageFor для отображения сообщений об ошибках рядом с полями формы. Например, @Html.ValidationMessageFor(m => m.Price) выведет ошибку, если значение Price выходит за пределы диапазона. Это улучшает UX, предоставляя мгновенную обратную связь. Принципы интеграции валидации в UI рассмотрены в лекции 2.
Пункт 11. Настройка маршрутизации через атрибут [Route]
Замените стандартную маршрутизацию на атрибутную, используя [Route] в контроллерах и действиях. Например, задайте маршрут [Route("products")] для ProductController, а для действия Details — [Route("details/{id}")]. Это повышает читаемость и позволяет гибко настраивать URL-структуру. Атрибутная маршрутизация обсуждается в лекции 1.
Пункт 12. Создание Razor-представлений для отображения данных
Создайте представления Index.cshtml и Create.cshtml в папке Views/Product. В Index отобразите список товаров через цикл @foreach, используя модель IEnumerable<Product>. В Create реализуйте форму с полями Name, Price и кнопкой отправки. Razor-синтаксис и связь с контроллерами описаны в лекции 1.
Пункт 13. Передача данных из контроллера в представление через ViewBag
Для передачи дополнительных данных (например, категорий товаров) в представление используйте ViewBag. В действии контроллера присвойте ViewBag.Categories = categoriesList, а в представлении обратитесь через @ViewBag.Categories. Хотя ViewModel предпочтительнее, ViewBag подходит для простых сценариев. Передача данных рассмотрена в лекции 1.
Пункт 14. Реализация промежуточного ПО для обработки запросов
Добавьте кастомный middleware для логирования времени выполнения запроса. В Program.cs используйте app.UseMiddleware<RequestTimingMiddleware>(). Middleware должен измерять время между получением запроса и отправкой ответа, записывая его в лог. Работа middleware и конвейера обработки запросов детально разобрана в лекции 1.
Пункт 15. Настройка конфигурации в appsettings.json
Добавьте в appsettings.json параметры, например, строку подключения к базе данных или настройки логгирования. Для доступа к ним в коде используйте IConfiguration, внедренный через DI. Например, Configuration.GetConnectionString("DefaultConnection"). Интеграция конфигурации описана в лекции 1.
Пункт 16. Интеграция среды разработки (Development/Production)
Настройте разные параметры для сред Development и Production через appsettings.Development.json и appsettings.Production.json. Например, в Development включите подробное логгирование, а в Production — режим сжатия статических файлов. Используйте IWebHostEnvironment для проверки текущей среды. Работа с окружениями рассмотрена в лекции 1.
Пункт 17. Реализация базового логгирования через ILogger
Внедрите ILogger<ProductController> в контроллер и добавьте логирование ключевых событий: ошибок валидации, успешного добавления товара. Используйте методы LogInformation, LogError и настройте уровни логирования в appsettings.json. Логгирование как часть инфраструктуры MVC описано в лекции 1.
Пункт 18. Создание страницы ошибки с обработкой исключений
Реализуйте middleware для обработки исключений через app.UseExceptionHandler("/Home/Error"). Создайте действие Error в HomeController и представление Error.cshtml, которое отображает сообщение об ошибке. Для деталей исключения в Development используйте Environment.IsDevelopment(). Обработка исключений через middleware разобрана в лекции 1.
Пункт 19. Тестирование привязки данных из разных источников
Проверьте, как Model Binding обрабатывает данные из маршрута, query string и тела запроса. Например, передайте id через URL (/product/5), filter через query string (?category=electronics) и данные формы (Name, Price). Убедитесь, что приоритет источников соблюдается (маршрут > query string > тело). Примеры тестирования приведены в лекции 2.
Пункт 20. Добавление кастомных сообщений об ошибках валидации
Измените атрибуты валидации, добавив параметр ErrorMessage. Например, [Required(ErrorMessage = "Название товара обязательно")]. Это улучшит понятность сообщений для пользователей. Кастомные сообщения и их локализация обсуждаются в лекции 2.
Пункт 21. Реализация сложной модели с вложенными свойствами
Создайте класс Order, содержащий свойства Customer (тип Customer) и List<OrderItem>. Настройте Model Binding так, чтобы данные формы (например, Customer.Name и OrderItems[0].ProductId) корректно привязывались к объекту Order. Рекурсивный биндинг сложных объектов описан в лекции 2.
Пункт 22. Настройка фильтрации данных через Model Binding
Добавьте параметр [BindNever] к свойству IsAdmin в модели User, чтобы предотвратить overposting. Альтернативно, используйте [Bind("Name, Price")] в действии контроллера, чтобы разрешить привязку только указанных полей. Методы защиты от перезаписи свойств рассмотрены в лекции 2.
Пункт 23. Тестирование различных сценариев маршрутизации
Проверьте работу атрибутной и конвенциональной маршрутизации. Например, создайте действие с [Route("api/products/{id}")] и убедитесь, что запрос /api/products/5 корректно обрабатывается. Также протестируйте маршруты с необязательными параметрами и значениями по умолчанию. Принципы маршрутизации изложены в лекции 1.
Пункт 24. Интеграция статических файлов через UseStaticFiles
Включите поддержку статических файлов (CSS, изображения) через app.UseStaticFiles() в Program.cs. Разместите файлы в папке wwwroot и убедитесь, что они доступны по URL вида /css/site.css. Настройте UseStaticFiles до UseRouting, чтобы статические файлы обрабатывались до маршрутизации. Работа со статическими файлами описана в лекции 1.
Пункт 25. Документирование проекта и создание README
Напишите README.md, включающий описание проекта, инструкции по запуску, зависимости и примеры API-эндпоинтов. Добавьте комментарии в код, объясняющие ключевые решения (например, выбор DI-жизненного цикла сервисов). Документирование как часть процесса разработки упоминается в контексте поддерживаемости в лекции 1.
Лабораторная работа 2: Слой данных и авторизация
Пункт 1. Настройка EF Core с SQL Server
Для подключения к базе данных SQL Server необходимо установить пакеты NuGet: Microsoft.EntityFrameworkCore и Microsoft.EntityFrameworkCore.SqlServer. В файле Program.cs регистрируется DbContext через метод AddDbContext, где указывается строка подключения, извлеченная из appsettings.json. Например, строка "Server=.;Database=ShopDb;Trusted_Connection=True;" передается в метод UseSqlServer. Это обеспечивает интеграцию ORM с приложением, позволяя выполнять запросы через объекты C#. Конфигурация подключения и регистрация сервисов описаны в лекции 3, раздел о настройке EF Core.
Пункт 2. Создание классов сущностей и DbContext
Определите классы сущностей, такие как Product (Id, Name, Price) и Order (Id, UserId, Total), которые будут отображаться на таблицы базы данных. Создайте класс AppDbContext, наследующийся от DbContext, и определите DbSet<Product> Products и DbSet<Order> Orders. Используйте Fluent API в методе OnModelCreating для настройки связей, например, между Order и Product через промежуточную сущность OrderItem. Примеры конфигурации сущностей приведены в лекции 3.
Пункт 3. Реализация миграций через Add-Migration
Используйте команду Add-Migration InitialCreate в консоли диспетчера пакетов, чтобы сгенерировать миграцию на основе созданных сущностей. Миграция создаст класс с методами Up (для применения изменений) и Down (для отката). Затем выполните Update-Database, чтобы обновить схему базы данных. Миграции позволяют синхронизировать код и базу данных, что подробно описано в лекции 3.
Пункт 4. Создание репозитория для CRUD-операций
Создайте интерфейс IProductRepository с методами GetByIdAsync, GetAllAsync, AddAsync, UpdateAsync и DeleteAsync. Реализуйте его в классе ProductRepository, используя AppDbContext для взаимодействия с базой. Например, метод AddAsync вызывает _context.Products.AddAsync(product). Репозиторий инкапсулирует логику доступа к данным, что соответствует паттерну Repository из лекции 3.
Пункт 5. Интеграция репозитория в DI-контейнер
В Program.cs зарегистрируйте репозиторий как сервис: services.AddScoped<IProductRepository, ProductRepository>(). Это обеспечит внедрение зависимости в контроллеры через конструктор. DI-контейнер управляет жизненным циклом репозитория, что способствует тестируемости и слабой связанности. Принципы DI рассмотрены в лекции 3.
Пункт 6. Реализация Unit of Work для транзакций
Создайте интерфейс IUnitOfWork с методами CommitAsync и RollbackAsync, а также свойствами для репозиториев (например, Products). Реализуйте его в классе UnitOfWork, который агрегирует AppDbContext и репозитории. Метод CommitAsync вызывает SaveChangesAsync, обеспечивая атомарность операций. Пример использования Unit of Work приведен в лекции 3.
Пункт 7. Тестирование работы репозитория с базой данных
Напишите тесты для методов репозитория, используя InMemory-провайдер EF Core. Например, проверьте, что метод AddAsync сохраняет продукт в базу. Убедитесь, что GetAllAsync возвращает корректное количество записей. Тестирование слоя данных описано в лекции 3 в разделе о практических аспектах.
Пункт 8. Настройка Identity для аутентификации
Установите пакет Microsoft.AspNetCore.Identity.EntityFrameworkCore и добавьте сервисы Identity в Program.cs: services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<AppDbContext>(). Это интегрирует Identity с вашим DbContext, создавая таблицы для пользователей и ролей. Настройка Identity обсуждается в лекции 4.
Пункт 9. Создание ролей User и Admin через RoleManager
В методе инициализации приложения (например, в классе SeedData) используйте RoleManager<IdentityRole> для проверки существования ролей. Если ролей нет, создайте их: await _roleManager.CreateAsync(new IdentityRole("Admin")). Роли сохраняются в таблице AspNetRoles. Пример кода приведен в лекции 4.
Пункт 10. Реализация регистрации пользователя
Создайте контроллер AccountController с действием Register (POST), принимающим модель RegisterViewModel. Используйте UserManager<IdentityUser> для создания пользователя: var result = await _userManager.CreateAsync(user, model.Password). При успехе назначьте роль User: await _userManager.AddToRoleAsync(user, "User"). Процесс регистрации описан в лекции 4.
Пункт 11. Настройка Cookie-аутентификации через IdentityOptions
В Program.cs настройте параметры Identity:
services.Configure<IdentityOptions>(options => {
options.Password.RequireDigit = true;
options.Cookies.ApplicationCookie.ExpireTimeSpan = TimeSpan.FromHours(2);
});
Это усиливает безопасность, устанавливая требования к паролю и время жизни куки. Детали настройки приведены в лекции 4.
Пункт 12. Интеграция атрибута [Authorize] в контроллеры
Пометьте контроллер AdminController атрибутом [Authorize(Roles = "Admin")], чтобы ограничить доступ только администраторам. Для отдельных методов используйте [Authorize], чтобы требовать аутентификации. Примеры применения атрибутов описаны в лекции 4.
Пункт 13. Создание политики доступа для роли Admin
В Program.cs добавьте политику:
services.AddAuthorization(options => {
options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
});
Примените её к действиям через [Authorize(Policy = "AdminOnly")]. Политики позволяют гибко управлять доступом, как указано в лекции 4.
Пункт 14. Реализация страницы "Доступ запрещен"
Создайте действие AccessDenied в AccountController и представление AccessDenied.cshtml. Настройте перенаправление при ошибке доступа через services.ConfigureApplicationCookie(options => options.AccessDeniedPath = "/Account/AccessDenied"). Пример обработки ошибок приведен в лекции 4.
Пункт 15. Настройка требований к паролю (длина, символы)
В IdentityOptions установите:
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = true;
Это гарантирует, что пароли будут соответствовать минимальным требованиям безопасности. Настройки паролей детализированы в лекции 4.
Пункт 16. Обработка блокировки аккаунта при неудачных попытках входа
Активируйте блокировку в IdentityOptions:
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
При превышении попыток входа аккаунт блокируется. Защита от брутфорс-атак описана в лекции 4.
Пункт 17. Интеграция Identity с существующими моделями
Добавьте связь между классом User (IdentityUser) и Order через свойство public string UserId в Order. Настройте связь в DbContext: modelBuilder.Entity<Order>().HasOne(u => u.User).WithMany(o => o.Orders). Это объединяет пользовательские данные с бизнес-логикой. Интеграция сущностей рассмотрена в лекции 4.
Пункт 18. Реализация пользовательского интерфейса для управления ролями
Создайте представление RoleManagement.cshtml, где администратор может назначать роли пользователям через выпадающие списки. Используйте RoleManager и UserManager для обновления ролей. Пример управления ролями описан в лекции 4.
Пункт 19. Настройка защиты от CSRF через [ValidateAntiForgeryToken]
Добавьте атрибут [ValidateAntiForgeryToken] к POST-действиям, например, в методах CreateOrder или EditProduct. Это предотвращает подделку межсайтовых запросов. Защита от CSRF обсуждается в лекции 4.
Пункт 20. Реализация выхода пользователя (Logout)
Добавьте действие Logout в AccountController:
public async Task<IActionResult> Logout() {
await _signInManager.SignOutAsync();
return RedirectToAction("Index", "Home");
}
Ссылка на выход размещается в навигационном меню. Выход из системы описан в лекции 4.
Пункт 21. Тестирование транзакций через Unit of Work
Создайте тест, где несколько операций (добавление продукта и создание заказа) выполняются в рамках одной транзакции. Убедитесь, что при ошибке изменения откатываются. Примеры тестирования Unit of Work приведены в лекции 3.
Пункт 22. Создание представлений для административных функций
Разработайте представления AdminPanel.cshtml и UserManagement.cshtml, используя Razor-синтаксис. Отображайте списки пользователей и товаров, добавляя кнопки для редактирования и удаления. Связь представлений с контроллерами описана в лекции 4.
Пункт 23. Интеграция репозиториев с Identity-сервисами
Измените ProductRepository, чтобы он зависел от UserManager<IdentityUser>, если требуется доступ к данным пользователя. Зарегистрируйте зависимости в DI-контейнере. Пример интеграции сервисов приведен в лекции 3.
Пункт 24. Тестирование авторизации для разных ролей
Проверьте доступ к защищенным маршрутам под разными ролями. Например, убедитесь, что пользователь с ролью User не может открыть /admin/panel. Используйте Postman для отправки запросов с куки аутентификации. Тестирование авторизации описано в лекции 4.
Пункт 25. Документирование работы слоя данных и авторизации
Напишите раздел в README.md, описывающий структуру базы данных, миграции, политики доступа и настройки Identity. Добавьте схемы взаимодействия компонентов и примеры запросов. Документирование как часть процесса разработки упоминается в лекциях 3 и 4.
Лабораторная работа 3: Бизнес-логика и RESTful API
Пункт 1. Создание сервисного слоя для бизнес-логики
Создайте класс ProductService, который будет содержать методы для работы с товарами: добавление, обновление, удаление и получение данных. Например, метод AddProductAsync проверяет, существует ли товар с таким же названием, и вызывает исключение при дублировании. Сервисный слой инкапсулирует бизнес-правила, такие как проверка минимальной цены или доступности товара. Взаимодействие с репозиториями (IProductRepository) происходит через DI. Пример разделения ответственности между контроллером и сервисом описан в лекции 5, раздел о взаимодействии слоев.
Пункт 2. Реализация интерфейсов для сервисов
Определите интерфейс IProductService с методами AddProductAsync, GetProductByIdAsync и другими, соответствующими бизнес-логике. Реализуйте этот интерфейс в классе ProductService, внедряя зависимости через конструктор. Интерфейсы обеспечивают абстракцию, что упрощает замену реализации и тестирование. Пример использования интерфейсов сервисов приведен в лекции 5.
Пункт 3. Интеграция сервисов в DI-контейнер
В файле Program.cs зарегистрируйте сервисы: services.AddScoped<IProductService, ProductService>(). Это позволяет внедрять сервисы в контроллеры через конструкторы, обеспечивая слабую связанность. Настройка DI-контейнера для сервисов описана в лекции 5, раздел о внедрении зависимостей.
Пункт 4. Реализация Exception Filter для глобальной обработки ошибок
Создайте кастомный фильтр BusinessExceptionFilter, наследующийся от IExceptionFilter. В методе OnException перехватывайте исключения типа BusinessException и возвращайте ответ с кодом 400 и сообщением об ошибке. Зарегистрируйте фильтр глобально через services.AddControllers(options => options.Filters.Add<BusinessExceptionFilter>()). Пример реализации фильтров исключений приведен в лекции 5.
Пункт 5. Создание кастомных исключений для бизнес-правил
Определите класс BusinessException, наследующийся от Exception. Используйте его в сервисах для выбрасывания ошибок при нарушении бизнес-правил, например, при попытке добавить товар с отрицательной ценой. Кастомные исключения улучшают читаемость кода и упрощают обработку ошибок.
Пункт 6. Валидация бизнес-правил через FluentValidation
Установите пакет FluentValidation и создайте класс ProductValidator, наследующийся от AbstractValidator<Product>. Добавьте правила: RuleFor(p => p.Price).GreaterThan(0).WithMessage("Цена должна быть положительной"). Интеграция FluentValidation в ASP.NET Core описана в лекции 5.
Пункт 7. Интеграция FluentValidation в ASP.NET Core
В Program.cs зарегистрируйте валидаторы: services.AddValidatorsFromAssemblyContaining<ProductValidator>(). В контроллерах используйте validator.ValidateAsync(model) для проверки данных перед выполнением операций. Пример использования FluentValidation приведен в лекции 5.
Пункт 8. Реализация RESTful-контроллеров с методами GET, POST, PUT, DELETE
Создайте контроллер ProductsController с методами, помеченными атрибутами [HttpGet], [HttpPost], [HttpPut], [HttpDelete]. Например, метод [HttpGet("{id}")] возвращает товар по ID. Используйте сервисный слой для выполнения операций. Пример RESTful-методов описан в лекции 6.
Пункт 9. Настройка Content Negotiation для JSON и XML
В Program.cs добавьте поддержку XML-форматера: services.AddControllers().AddXmlSerializerFormatters(). Теперь API будет возвращать данные в формате JSON или XML в зависимости от заголовка Accept. Настройка форматеров рассмотрена в лекции 6.
Пункт 10. Сериализация данных через System.Text.Json
Используйте атрибуты [JsonPropertyName] и [JsonIgnore] для управления сериализацией. Например, [JsonPropertyName("productName")] изменит имя поля в JSON. Сериализация через System.Text.Json описана в лекции 6.
Пункт 11. Создание DTO для передачи данных в API
Определите класс ProductDTO с полями Id, Name и Price. Используйте его в методах контроллера вместо моделей базы данных, чтобы скрыть внутреннюю структуру. Маппинг между DTO и моделями выполняется через AutoMapper или вручную.
Пункт 12. Реализация пагинации в RESTful-методах
Добавьте параметры pageNumber и pageSize в метод [HttpGet]. Используйте LINQ-методы Skip и Take для разбиения данных. Возвращайте объект PaginatedResult, содержащий данные и метаинформацию. Пример пагинации описан в лекции 6.
Пункт 13. Настройка версионирования API через атрибуты
Установите пакет Microsoft.AspNetCore.Mvc.Versioning. В Program.cs настройте версионирование: services.AddApiVersioning(options => options.DefaultApiVersion = new ApiVersion(1, 0)). Пометите контроллер атрибутом [ApiVersion("1.0")]. Версионирование API рассмотрено в лекции 6.
Пункт 14. Интеграция валидации моделей в API-контроллеры
Используйте ModelState.IsValid в методах контроллера для проверки валидности входных данных. При ошибках возвращайте BadRequest(ModelState). Пример валидации через DataAnnotations приведен в лекции 6.
Пункт 15. Реализация асинхронных методов в сервисах
Измените методы сервисов на асинхронные, используя ключевое слово async и await. Например, public async Task<Product> GetProductByIdAsync(int id). Асинхронные операции улучшают производительность, как описано в лекции 5.
Пункт 16. Тестирование API через Postman
Создайте коллекцию запросов в Postman для проверки всех эндпоинтов: GET /products, POST /products, PUT /products/{id}. Убедитесь, что возвращаются корректные статусы и данные. Тестирование RESTful-API описано в лекции 6.
Пункт 17. Настройка кэширования ответов API
Добавьте атрибут [ResponseCache(Duration = 60)] к методам GET, чтобы кэшировать ответы на стороне клиента. Для серверного кэширования используйте IDistributedCache. Кэширование упоминается в лекции 5 в контексте оптимизации.
Пункт 18. Реализация сортировки данных в RESTful-методах
Добавьте параметр sortBy в метод GET. Используйте LINQ-метод OrderBy для сортировки данных. Например: products.OrderBy(p => p.Name). Пример сортировки через параметры запроса описан в лекции 6.
Пункт 19. Интеграция бизнес-логики с Unit of Work
Измените сервисы для использования IUnitOfWork вместо отдельных репозиториев. Например, в методе AddProductAsync вызовите _unitOfWork.Products.AddAsync(product), затем _unitOfWork.CommitAsync(). Интеграция Unit of Work рассмотрена в лекции 3.
Пункт 20. Создание документации API через Swagger
Установите пакет Swashbuckle.AspNetCore. В Program.cs добавьте services.AddSwaggerGen() и app.UseSwagger(). Настройте описание API с помощью атрибутов [SwaggerOperation]. Документация через Swagger описана в лекции 6.
Пункт 21. Тестирование обработки ошибок через Exception Filters
Сымитируйте ошибку, например, передав невалидные данные в POST-запрос. Убедитесь, что фильтр BusinessExceptionFilter возвращает статус 400 с сообщением. Пример тестирования фильтров приведен в лекции 5.
Пункт 22. Реализация кастомных сериализаторов для сложных объектов
Создайте класс CustomJsonConverter для сериализации объектов с циклическими ссылками. Зарегистрируйте его через services.AddControllers().AddJsonOptions(options => options.JsonSerializerOptions.Converters.Add(new CustomJsonConverter()). Настройка сериализации описана в лекции 6.
Пункт 23. Настройка политик авторизации для API
Добавьте политику "AdminOnly" через services.AddAuthorization() и примените её к методам контроллера с помощью [Authorize(Policy = "AdminOnly")]. Примеры политик авторизации приведены в лекции 4.
Пункт 24. Рефакторинг кода для соблюдения SOLID
Проверьте, что каждый класс имеет одну ответственность, интерфейсы абстрагируют реализацию, а зависимости внедряются через DI. Устраните дублирование кода, выделив общую логику в базовые классы. Принципы SOLID обсуждаются в лекции 5.
Пункт 25. Документирование RESTful-эндпоинтов
Добавьте комментарии к методам контроллера с использованием XML-документации. Включите описание параметров, возвращаемых значений и примеры запросов. Сгенерируйте документацию через Swagger. Детали документирования API приведены в лекции 6.
Лабораторная работа 4: Расширенные возможности контроллеров
Пункт 1. Реализация глобального Action Filter для логирования действий
Создайте класс GlobalLoggingFilter, реализующий интерфейс IActionFilter. В методе OnActionExecuting запишите в лог информацию о начале выполнения действия (например, имя контроллера и метода). В OnActionExecuted зафиксируйте время выполнения и статус результата. Зарегистрируйте фильтр глобально в Program.cs через services.AddControllers(options => options.Filters.Add<GlobalLoggingFilter>()). Используйте ILogger для логирования, внедренный через конструктор. Глобальная фильтрация действий и примеры кода приведены в лекции 7.
Пункт 2. Создание кастомного Route-атрибута для динамической маршрутизации
Создайте класс ApiVersionRouteAttribute, наследующийся от RouteAttribute. Переопределите свойство Template, чтобы включать версию API (например, "api/v{version}/[controller]"). Используйте атрибут в контроллере: [ApiVersionRoute("1")]. Это позволяет динамически формировать маршруты для разных версий API. Примеры кастомных маршрутов описаны в лекции 7.
Пункт 3. Настройка порядка выполнения фильтров через свойство Order
Для управления порядком выполнения фильтров задайте свойство Order в их атрибутах. Например, фильтр аутентификации должен выполняться до фильтра логирования: [ServiceFilter(typeof(AuthenticationFilter), Order = 1]. Проверьте порядок в конвейере обработки запроса. Управление порядком фильтров рассмотрено в лекции 7.
Пункт 4. Реализация загрузки файлов через IFormFile
Создайте действие в контроллере, принимающее параметр типа IFormFile. В методе обработайте загруженный файл: проверьте его наличие, сохраните во временную папку или обработайте содержимое. Используйте атрибут [HttpPost] и форму с enctype="multipart/form-data". Пример работы с IFormFile приведен в лекции 7.
Пункт 5. Валидация MIME-типов и размера файлов
Добавьте проверку MIME-типа файла через свойство ContentType. Например, разрешите только image/jpeg и image/png. Ограничьте размер файла атрибутом [RequestSizeLimit(10_000_000)] (10 МБ). При несоответствии возвращайте BadRequest. Валидация файлов описана в лекции 7.
Пункт 6. Сохранение загруженных файлов в файловой системе
Используйте IHostingEnvironment для получения пути к папке wwwroot/files. Сохраняйте файл через FileStream, генерируя уникальное имя (например, Guid + расширение). Убедитесь, что папка существует, иначе создайте её. Пример сохранения файлов приведен в лекции 7.
Пункт 7. Реализация скачивания файлов через FileResult
Создайте действие, возвращающее FileResult. Прочитайте файл из файловой системы в byte[] и верните его через File(fileBytes, "application/octet-stream", fileName). Для больших файлов используйте потоковую передачу. Примеры использования FileResult описаны в лекции 7.
Пункт 8. Интеграция фильтров для проверки прав доступа к файлам
Создайте фильтр FileAccessFilter, проверяющий, имеет ли пользователь право на доступ к файлу. В OnActionExecuting извлеките параметр (например, fileId) и проверьте права через UserManager. Если доступ запрещен, установите context.Result = Forbid(). Интеграция фильтров для файлов описана в лекции 7.
Пункт 9. Создание асинхронного Action Filter
Реализуйте класс AsyncLoggingFilter, наследующийся от IAsyncActionFilter. В методе OnActionExecutionAsync используйте await next() для асинхронной обработки. Замерьте время выполнения и залогируйте результат. Асинхронные фильтры рассмотрены в лекции 7.
Пункт 10. Реализация фильтра для измерения времени выполнения запросов
Создайте класс ExecutionTimeFilter с таймером в OnActionExecuting. В OnActionExecuted вычислите разницу времени и запишите её в лог. Зарегистрируйте фильтр глобально. Пример измерения времени выполнения приведен в лекции 7.
Пункт 11. Настройка ограничений на загрузку больших файлов
Используйте атрибут [RequestSizeLimit] в действиях загрузки файлов. Например, [RequestSizeLimit(100_000_000)] ограничивает размер до 100 МБ. При превышении клиент получит ошибку 413 Payload Too Large. Ограничения размера файлов описаны в лекции 7.
Пункт 12. Интеграция кастомных маршрутов в API-контроллеры
Примените созданный ранее ApiVersionRouteAttribute к API-контроллерам. Например, [ApiVersionRoute("2", "products")] сгенерирует маршрут /api/v2/products. Убедитесь, что маршруты корректно обрабатываются. Примеры интеграции кастомных маршрутов приведены в лекции 7.
Пункт 13. Тестирование загрузки и скачивания файлов
Используйте Postman для отправки POST-запросов с файлами и GET-запросов для скачивания. Проверьте корректность сохранения, MIME-типы и возвращаемые статусы. Тестирование файловых операций описано в лекции 7.
Пункт 14. Реализация потоковой передачи файлов
Измените метод скачивания файла, используя FileStreamResult. Откройте файл через FileStream в режиме чтения и верните его через File(fileStream, "application/octet-stream"). Это снижает нагрузку на память при работе с большими файлами. Потоковая передача упоминается в лекции 7.
Пункт 15. Настройка безопасности при работе с файлами (GUID-имена)
При сохранении файлов генерируйте уникальные имена через Guid.NewGuid(). Это предотвращает перезапись и атаки подбора имен. Например: string fileName = $"{Guid.NewGuid()}{Path.GetExtension(file.FileName)}". Безопасность файлов рассмотрена в лекции 7.
Пункт 16. Интеграция файловых операций в бизнес-логику
Добавьте в сервисный слой методы для обработки файлов. Например, метод UploadProductImageAsync может сохранять изображение товара и обновлять запись в базе данных. Используйте Unit of Work для атомарности операций. Интеграция с бизнес-логикой описана в лекции 7.
Пункт 17. Создание промежуточного ПО для обработки файлов
Реализуйте middleware FileProcessingMiddleware, которое проверяет запросы на наличие файлов и логирует их метаданные. Зарегистрируйте middleware в конвейере через app.UseMiddleware<FileProcessingMiddleware>(). Примеры middleware приведены в лекции 7.
Пункт 18. Тестирование конфликтов маршрутизации
Проверьте, что кастомные маршруты не конфликтуют со стандартными. Например, убедитесь, что /api/v1/products и /products обрабатываются разными контроллерами. Используйте инструменты маршрутизации (например, app.MapControllers().UseRouting()). Конфликты маршрутизации упоминаются в лекции 7.
Пункт 19. Реализация динамических параметров в маршрутах
Добавьте в кастомный атрибут маршрута динамические параметры, например, {category}. Используйте их в действии: [DynamicRoute("products/{category}")]. Это позволяет создавать гибкие URL, такие как /products/electronics. Динамические параметры описаны в лекции 7.
Пункт 20. Настройка кэширования загруженных файлов
Используйте IDistributedCache для кэширования метаданных файлов. Например, при загрузке файла сохраните его имя в кэш с ключом на основе GUID. При скачивании проверяйте кэш перед обращением к файловой системе. Кэширование упоминается в лекции 7.
Пункт 21. Интеграция работы с файлами в RESTful API
Добавьте эндпоинты для загрузки и скачивания файлов в RESTful-контроллеры. Например, POST /api/products/{id}/image для загрузки изображения товара. Используйте стандартные HTTP-методы и статусы. Интеграция с RESTful API описана в лекции 7.
Пункт 22. Реализация фильтра для обработки ошибок при загрузке
Создайте фильтр UploadExceptionFilter, перехватывающий исключения при загрузке файлов (например, IOException). Возвращайте статус 500 с сообщением об ошибке. Зарегистрируйте фильтр глобально. Примеры обработки ошибок приведены в лекции 7.
Пункт 23. Тестирование производительности при работе с файлами
Замерьте время загрузки и скачивания файлов разного размера (1 МБ, 100 МБ). Используйте профилировщик (например, Application Insights) для выявления узких мест. Оптимизируйте код, используя асинхронные методы и потоковую передачу. Тестирование производительности упоминается в лекции 7.
Пункт 24. Настройка CORS для доступа к файлам из внешних источников
В Program.cs настройте политику CORS, разрешающую запросы с определенных доменов: services.AddCors(options => options.AddPolicy("AllowOrigin", builder => builder.WithOrigins("https://example.com"))). Примените политику к контроллерам через [EnableCors("AllowOrigin")]. Настройка CORS описана в лекции 7.
Пункт 25. Документирование расширенных возможностей контроллеров
Добавьте в Swagger описание эндпоинтов для загрузки и скачивания файлов, кастомных маршрутов и фильтров. Используйте атрибуты [SwaggerOperation] и XML-комментарии. Сгенерируйте документацию через Swagger UI. Документирование API рассмотрено в лекции 7.
Лабораторная работа 5: Мониторинг и диагностика
Пункт 1. Настройка логирования через ILogger с категориями
Для настройки логирования с категориями внедрите ILogger<T> в контроллеры или сервисы, где T — это класс, к которому относятся логи (например, ILogger<OrderService>). Категория автоматически присваивается как полное имя класса. В Program.cs настройте фильтрацию логов через appsettings.json, указав минимальные уровни для конкретных категорий. Например, для категории "Microsoft.EntityFrameworkCore" установите уровень Warning. Примеры настройки категорий приведены в лекции 8.
Пункт 2. Реализация уровней логирования (Trace, Debug, Error)
В файле appsettings.json задайте минимальный уровень логирования для разных провайдеров. Например, для консоли установите "Default": "Information", а для файлового провайдера — "Trace". Используйте методы _logger.LogTrace(), _logger.LogDebug() и _logger.LogError() для записи сообщений соответствующего уровня. Уровни логирования и их применение описаны в лекции 8.
Пункт 3. Интеграция Serilog для записи логов в файл
Установите пакеты Serilog.AspNetCore и Serilog.Sinks.File. В Program.cs настройте Serilog через Host.CreateDefaultBuilder().UseSerilog(). Укажите путь к файлу логов и формат записи. Например, Log.Logger = new LoggerConfiguration().WriteTo.File("logs/app.log").CreateLogger(). Интеграция Serilog рассмотрена в лекции 8.
Пункт 4. Создание диагностического Middleware для логирования запросов
Создайте класс RequestLoggingMiddleware с методом InvokeAsync, который логирует метод, путь и статус ответа. Замерьте время выполнения запроса через Stopwatch. Зарегистрируйте middleware в конвейере через app.UseMiddleware<RequestLoggingMiddleware>(). Пример middleware для логирования запросов приведен в лекции 8.
Пункт 5. Настройка мониторинга через dotnet-counters
Установите dotnet-counters как глобальный инструмент: dotnet tool install --global dotnet-counters. Запустите мониторинг метрик для процесса приложения: dotnet-counters monitor --process-id <PID> --providers Microsoft.AspNetCore.Hosting. Это позволит отслеживать количество запросов, ошибок и использование памяти. Использование dotnet-counters описано в лекции 8.
Пункт 6. Реализация Health Checks для проверки состояния сервисов
Добавьте сервисы Health Checks в Program.cs: services.AddHealthChecks().AddCheck<DatabaseHealthCheck>("database"). Создайте эндпоинт через app.MapHealthChecks("/health"). Реализуйте класс DatabaseHealthCheck, проверяющий подключение к базе данных. Пример Health Checks приведен в лекции 8.
Пункт 7. Настройка синхронизации доступа к общим ресурсам через lock
Для защиты общего ресурса (например, счетчика) используйте ключевое слово lock. Объявите объект-блокировку: private static readonly object _lock = new object(). В методе IncrementCounter оберните критическую секцию в lock (_lock). Пример синхронизации через lock описан в лекции 8.
Пункт 8. Интеграция потокобезопасных коллекций (ConcurrentDictionary)
Замените обычный Dictionary на ConcurrentDictionary<int, string> для работы в многопоточной среде. Используйте методы TryAdd и TryGetValue для безопасного доступа. Потокобезопасные коллекции из System.Collections.Concurrent рассмотрены в лекции 8.
Пункт 9. Реализация обработки многопоточных исключений
В асинхронных методах обрабатывайте исключения внутри задач через try/catch. Например, в Task.Run(() => { try { ... } catch (Exception ex) { _logger.LogError(ex, "Ошибка в задаче"); } }). Для агрегирования исключений используйте await Task.WhenAll с обработкой AggregateException. Примеры обработки исключений приведены в лекции 8.
Пункт 10. Настройка Application Insights для телеметрии
Установите пакет Microsoft.ApplicationInsights.AspNetCore. В appsettings.json укажите ключ инструментирования: "ApplicationInsights": { "InstrumentationKey": "ваш-ключ" }. Данные телеметрии (запросы, исключения) будут автоматически отправляться в Application Insights. Интеграция с Application Insights описана в лекции 8.
Пункт 11. Создание пользовательских метрик производительности
Используйте TelemetryClient для отправки кастомных метрик. Например: _telemetryClient.TrackMetric("OrdersProcessed", ordersCount). Метрики можно группировать по измерениям, таким как регион или категория товара. Примеры работы с метриками приведены в лекции 8.
Пункт 12. Тестирование отладки многопоточных сценариев в Visual Studio
Запустите приложение в режиме отладки. Используйте окно "Parallel Stacks" для анализа потоков и "Threads Window" для установки точек останова. Проверьте состояние переменных в разных потоках для обнаружения дедлоков. Инструменты отладки описаны в лекции 8.
Пункт 13. Настройка алертов для критических ошибок
В портале Azure перейдите в раздел Application Insights вашего ресурса. Создайте новое правило оповещения, указав условие (например, более 10 ошибок за 5 минут) и действие (отправка email или уведомление в Slack). Примеры настройки алертов приведены в лекции 8.
Пункт 14. Реализация асинхронного логирования
Настройте Serilog для асинхронной записи логов через WriteTo.Async(config => config.File("logs/app.log")). Это предотвратит блокировку основного потока при записи в файл. Асинхронное логирование упоминается в лекции 8.
Пункт 15. Интеграция диагностических инструментов (dotnet-dump)
Установите dotnet-dump: dotnet tool install --global dotnet-dump. Для сбора дампа памяти выполните: dotnet-dump collect --process-id <PID>. Анализируйте дамп через Visual Studio или WinDbg. Использование dotnet-dump описано в лекции 8.
Пункт 16. Настройка централизованного хранения логов (ELK, Seq)
Для интеграции с ELK настройте Serilog для отправки логов в Logstash через пакет Serilog.Sinks.Http. Для Seq используйте Serilog.Sinks.Seq. Укажите URL сервера в конфигурации: WriteTo.Seq("http://seq-server:5341"). Централизованное хранение логов рассмотрено в лекции 8.
Пункт 17. Тестирование Health Checks через HTTP-эндпоинты
Отправьте GET-запрос на /health через Postman или браузер. Убедитесь, что ответ содержит статус "Healthy" или "Unhealthy" в зависимости от состояния зависимостей. Пример тестирования Health Checks приведен в лекции 8.
Пункт 18. Реализация дашборда для визуализации метрик
В Azure Application Insights создайте дашборд, добавив виджеты для ключевых метрик: время ответа, количество ошибок, использование CPU. Настройте автоматическое обновление данных. Примеры дашбордов описаны в лекции 8.
Пункт 19. Настройка оповещений через email/Slack
В Azure Application Insights настройте Action Group для отправки уведомлений. Добавьте канал Slack через вебхук или email-адрес. Свяжите Action Group с правилом оповещения. Интеграция с внешними системами упоминается в лекции 8.
Пункт 20. Интеграция мониторинга в существующий проект
Добавьте в существующий проект пакеты Serilog, Application Insights и Health Checks. Настройте логирование, Health Checks и телеметрию в Program.cs. Обновите appsettings.json и документацию. Пример интеграции описан в лекции 8.
Пункт 21. Тестирование обработки исключений в многопоточной среде
Создайте тестовый метод, который запускает несколько задач, генерирующих исключения. Убедитесь, что все исключения перехватываются и логируются. Используйте _logger.LogError() и проверьте записи в логах. Примеры тестирования многопоточности приведены в лекции 8.
Пункт 22. Реализация кэширования результатов Health Checks
Используйте MemoryCache для кэширования результатов Health Checks. Установите время жизни кэша (например, 30 секунд). В классе Health Check возвращайте закэшированный результат, если он актуален. Пример кэширования описан в лекции 8.
Пункт 23. Настройка фильтрации логов по категориям
В appsettings.json задайте фильтры для категорий. Например, "Logging": { "LogLevel": { "Microsoft.EntityFrameworkCore": "Warning" } }. Это отключит логи уровня Information для Entity Framework. Фильтрация логов рассмотрена в лекции 8.
Пункт 24. Интеграция Application Insights с бизнес-логикой
Добавьте TelemetryClient в сервисы и внедрите его в бизнес-логику. Отслеживайте ключевые события: _telemetryClient.TrackEvent("OrderCreated"). Настройте пользовательские метрики для анализа производительности. Интеграция с бизнес-логикой описана в лекции 8.
Пункт 25. Документирование системы мониторинга и диагностики
Создайте раздел в README.md, описывающий настройку логирования, Health Checks, Application Insights и инструментов диагностики. Включите примеры конфигураций, команды для сбора дампов и ссылки на дашборды. Документирование системы мониторинга упоминается в лекции 8.