Skip to content
This repository was archived by the owner on Nov 4, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
176 changes: 176 additions & 0 deletions students/IraRozhdestvenskaja/task_04/doc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
№ Лабораторная работа 04 - Интернет-магазин электроники
Описание проекта
ASP.NET Core MVC приложение для интернет-магазина электроники с аутентификацией через сессии.

Функциональность
Главная страница с популярными товарами

Страница со списком всех товаров

Детальная страница товара с ограничением маршрута {id:int}

Система аутентификации (фиктивная)

Защищенная страница заказов

Привязка моделей из формы

Запуск проекта

dotnet run --urls="http://localhost:5000"
Приложение будет доступно по адресу: http://localhost:5000

Тестовые данные для входа:
Логин: admin

Пароль: password

Проверка health endpoint:

http://localhost:5000/healthz
Скриншоты
1. Главная страница


URL: http://localhost:5000

Показывает популярные товары

Навигационное меню

Статус аутентификации

2. Страница входа


URL: http://localhost:5000/Home/Login

Форма аутентификации

Тестовые данные: admin/password

3. Успешная аутентификация


Сообщение "Вы вошли как администратор"

Появление кнопки "Мои заказы"

4. Защищенная страница заказов


URL: http://localhost:5000/Home/Orders

Доступна только после входа

Форма создания заказа

5. Health Check Endpoint


URL: http://localhost:5000/healthz

Возвращает "Healthy"

6. Список всех товаров


URL: http://localhost:5000/Home/List

Полный каталог товаров

7. Детали товара с ограничением {id:int}


URL: http://localhost:5000/Home/Details/1

Ограничение маршрута только для integer ID

8. Создание заказа (привязка модели)


Демонстрация привязки модели из формы

Отображение данных после отправки

9. Консоль запуска приложения


Команда dotnet run

Порт прослушивания: http://localhost:5000

Схема пайплайна запроса
text
Запрос браузера
Middleware Pipeline
├── Static Files
├── Routing
├── Session ← (проверка IsAuthenticated)
└── Authorization
Controller Processing
├── HomeController.Index()
├── HomeController.Login() [HttpPost]
├── HomeController.Orders() [CustomAuth]
└── Model Binding (Form → OrderModel)
View Rendering
├── Razor Pages
├── Layout (_Layout.cshtml)
└── Session Data в View
HTML Response
Проверка сессии
Как работает аутентификация:
Хранение в сессии: HttpContext.Session.SetString("IsAuthenticated", "true")

Проверка доступа: Атрибут [CustomAuth] проверяет сессию

Время жизни: 30 минут (настроено в Program.cs)

Данные сессии: Username, IsAuthenticated

Тестовый сценарий:
Перейти на /Home/Login

Ввести: admin / password

Проверить появление сообщения об успешном входе

Убедиться что доступна страница /Home/Orders

Проверить что без входа страница заказов недоступна

Код проверки сессии:
csharp
// Установка сессии при входе
HttpContext.Session.SetString("IsAuthenticated", "true");

// Проверка в атрибуте
var isAuthenticated = context.HttpContext.Session.GetString("IsAuthenticated");
if (isAuthenticated != "true") {
context.Result = new RedirectToActionResult("Login", "Home", null);
}
Структура проекта
Controllers/ - HomeController с действиями и атрибутом авторизации

Models/ - Product, LoginModel, OrderModel (привязка данных)

Views/Home/ - Razor страницы с использованием сессии

Program.cs - конфигурация пайплайна и сервисов (сессии)

Middleware - кастомная аутентификация через сессии

Технические особенности
Сессии: DistributedMemoryCache для хранения состояния

Маршрутизация: Ограничение {id:int} для Details страницы

Привязка моделей: Form → OrderModel в CreateOrder

Авторизация: Кастомный атрибут [CustomAuth]

Health Check: Endpoint /healthz для мониторинга
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using ElectronicStore.Models;

namespace ElectronicStore.Controllers
{
public class HomeController : Controller
{
private readonly List<Product> _products = new()
{
new Product { Id = 1, Name = "iPhone 15", Category = "Смартфоны", Price = 999.99m, Description = "Новейший смартфон от Apple", Stock = 50 },
new Product { Id = 2, Name = "Samsung Galaxy S24", Category = "Смартфоны", Price = 899.99m, Description = "Флагманский смартфон от Samsung", Stock = 30 },
new Product { Id = 3, Name = "MacBook Pro", Category = "Ноутбуки", Price = 1999.99m, Description = "Мощный ноутбук для профессионалов", Stock = 20 },
new Product { Id = 4, Name = "Sony WH-1000XM5", Category = "Наушники", Price = 349.99m, Description = "Беспроводные наушники с шумоподавлением", Stock = 100 }
};

public IActionResult Index()
{
ViewData["Title"] = "Главная - Магазин электроники";
ViewData["IsAuthenticated"] = HttpContext.Session.GetString("IsAuthenticated") == "true";
return View(_products.Take(3).ToList());
}

public IActionResult List()
{
ViewData["Title"] = "Список товаров";
ViewData["IsAuthenticated"] = HttpContext.Session.GetString("IsAuthenticated") == "true";
return View(_products);
}

[Route("Home/Details/{id:int}")]
public IActionResult Details(int id)
{
var product = _products.FirstOrDefault(p => p.Id == id);
if (product == null)
{
return NotFound();
}

ViewData["Title"] = $"Детали - {product.Name}";
ViewData["IsAuthenticated"] = HttpContext.Session.GetString("IsAuthenticated") == "true";
return View(product);
}

public IActionResult Login()
{
ViewData["Title"] = "Вход в систему";
return View();
}

[HttpPost]
public IActionResult Login(LoginModel model)
{
if (model.Username == "admin" && model.Password == "password")
{
HttpContext.Session.SetString("IsAuthenticated", "true");
HttpContext.Session.SetString("Username", model.Username);
return RedirectToAction("Index");
}

ViewData["ErrorMessage"] = "Неверное имя пользователя или пароль";
return View(model);
}

public IActionResult Logout()
{
HttpContext.Session.Remove("IsAuthenticated");
HttpContext.Session.Remove("Username");
return RedirectToAction("Index");
}

[CustomAuth]
public IActionResult Orders()
{
ViewData["Title"] = "Мои заказы";
ViewData["Username"] = HttpContext.Session.GetString("Username");
return View();
}

[HttpPost]
[CustomAuth]
public IActionResult CreateOrder(OrderModel order)
{
ViewData["Title"] = "Заказ создан";
ViewData["Username"] = HttpContext.Session.GetString("Username");
return View(order);
}
}

public class CustomAuthAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
var isAuthenticated = context.HttpContext.Session.GetString("IsAuthenticated");
if (isAuthenticated != "true")
{
context.Result = new RedirectToActionResult("Login", "Home", null);
return;
}
base.OnActionExecuting(context);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Mvc;

namespace ElectronicStore.Controllers
{
public class TestController : Controller
{
public IActionResult Ping()
{
return Content("Pong! Server is working.");
}

public IActionResult SessionTest()
{
HttpContext.Session.SetString("Test", "Session works!");
var testValue = HttpContext.Session.GetString("Test");
return Content($"Session test: {testValue}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace ElectronicStore.Models
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Category { get; set; } = string.Empty;
public decimal Price { get; set; }
public string Description { get; set; } = string.Empty;
public int Stock { get; set; }
}

public class LoginModel
{
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
}

public class OrderModel
{
public string ProductName { get; set; } = string.Empty;
public int Quantity { get; set; }
public string ShippingAddress { get; set; } = string.Empty;
}
}
24 changes: 24 additions & 0 deletions students/IraRozhdestvenskaja/task_04/src/Prodgekt/Prodgekt.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.5.2.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ElectronicStore", "ElectronicStore.csproj", "{C4C2E0A3-93A2-4FA5-CEAB-D758EBAB31E8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C4C2E0A3-93A2-4FA5-CEAB-D758EBAB31E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C4C2E0A3-93A2-4FA5-CEAB-D758EBAB31E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4C2E0A3-93A2-4FA5-CEAB-D758EBAB31E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4C2E0A3-93A2-4FA5-CEAB-D758EBAB31E8}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7F0BA592-D270-40D0-91E6-F0088316C900}
EndGlobalSection
EndGlobal
Loading