Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)
Статичні Активи: MapStaticAssets (ASP.NET Core 9.0)
Впродовж багатьох років в ASP.NET Core стандартом для роздачі файлів був підхід із UseStaticFiles(), який ми розглянули в попередньому розділі. Він працює добре: браузер запитує файл з папки wwwroot, сервер його знаходить (якщо він є) і відправляє.
Але у 2024 році разом із випуском ASP.NET Core 9.0 компанія Microsoft вирішила повністю переосмислити роботу зі статикою. Вони представили революційний метод — MapStaticAssets().
Чому знадобився новий підхід? Оптимізація! Сучасні веб-додатки повинні завантажуватися блискавично.
Проблеми UseStaticFiles
У класичного підходу є кілька суттєвих недоліків для високо навантажених додатків:
- Звернення до диска (Disk I/O): На кожен запит (навіть якщо файл кешується) Middleware
UseStaticFilesнамагається перевірити фізичну наявність файлу на жорсткому диску. Диск завжди повільніший за оперативну пам'ять. - Кешування (Cache Busting): Якщо ви зміните
app.jsі викладете його на сервер, браузер користувача може не знати про це і використовувати стару закешовану версію з локального диска. Раніше для вирішення цього використовували хелпери (в Razor Pages), які додавали щось на кшталт?v=xyz123до URL, але в Minimal APIs з цим доводилось працювати вручну. - Стиснення (Compression): Файли часто можна стиснути за алгоритмами GZIP або Brotli, щоб вони стали меншими і вантажилися швидше. Але стиснення "на льоту" (під час запиту) забирає ресурси процесора (CPU).
Оптимізація під час збірки (Build-Time Optimization)
MapStaticAssets() кардинально відрізняється своїм підходом. Більшу частину роботи він робить не тоді, коли користувач запитує сторінку (Run-time), а тоді, коли ви натискаєте кнопку БІЛД (Build-time) у вашому IDE!
Під час створення білду або публікації проєкту (.NET SDK), фреймворк автоматично:
- Знаходить всі файли в
wwwroot. - Рахує криптографічний хеш для кожного файлу (унікальний код на основі його вмісту).
- Стискає текстові файли (JS, CSS, HTML, SVG) одразу у два попередньо збережені формати:
.gz(GZIP) та.br(Brotli).
Після цього він генерує оптимізовану "карту файлів" прямо в кінцеву збірку вашого коду (Manifest).
Використання MapStaticAssets()
У коді все виглядає навіть простіше, ніж зі старою системою.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Використовуємо новий революційний endpoint-роутинг для файлів (ASP.NET Core 9.0+)
app.MapStaticAssets();
app.MapGet("/", () => "Hello from API");
app.Run();
Анатомія коду:
- Ми викликаємо
MapStaticAssets()на екземпляріapp. - Зверніть увагу: ми використовуємо
Map...(як для звичайних роутівMapGet), а неUse.... Це означає, що статичні активи тепер тісно інтегровані в єдину систему Endpoint Routing. - Сервер завантажує свою "карту файлів" в оперативну пам'ять при старті. Коли приходить запит на
/js/app.js, серверу не потрібно йти на жорсткий диск, щоб перевірити, чи там є файл! Він дивиться в свою таблицю в пам'яті (пам'ять у 100 000 разів швидша за SSD), одразу знаходить файл і знає, що до нього існують готові стиснуті версії.
Вбудований Fingerprinting (Відбитки)
Оскільки під час збірки для кожного файлу генерується унікальний хеш, Microsoft додала механізм, який називається "Кешування з відбитками пальців" (Fingerprinting).
Коли файл публікується, його унікальний хеш можна включити безпосередньо у посилання на нього.
Наприклад, замість /css/main.css, браузер буде підвантажувати щось на зразок /css/main.{hash}.css.
Що це дає?
- Ми можемо наказати браузеру: "Кешуй цей файл НАЗАВЖДИ (на рік)!". Це робить завантаження наступних сторінок миттєвим.
- Але що, якщо нам треба оновити дизайн? Ми просто змінюємо
main.cssі компілюємо проєкт. - Хеш змінюється. Новий URL стає
/css/main.{newhash}.css. Сервер висилає нову сторінку. Браузер бачить, що URL файлу змінився, і завантажить його наново, не використовуючи старий кеш!
Якщо ви хочете згенерувати посилання на такий ресурс з коду або з інших endpoint-ів, зазвичай використовують
LinkGeneratorабо вбудовані хелпери в Razor/Blazor. Цей процес виходить за рамки базових Minimal APIs, але важливо розуміти, що файли тепер захищені від "застрягання в старому кеші браузера".
Вбудоване стиснення (Brotli & Gzip)
Завдяки тому, що SDK вже створив попередньо стиснуті (pre-compressed) версії файлів під час збірки, MapStaticAssets не витрачає ресурси процесора сервера на архівацію.
- Браузер надсилає запит і каже:
Accept-Encoding: gzip, deflate, br. (Це означає: "я вмію розпаковувати Brotli та GZIP"). - Сервер бачить
br(Brotli — найкраще стиснення), перевіряє свою таблицю в пам'яті: "Ага, в мене вже є готовий файл розміром не 150 КБ, а 30 КБ в пам'яті". І віддає браузеру ці 30 КБ. - Профіт: зниження мережевого трафіку до 80% і зменшення навантаження на процесори серверів.
Коли потрібно повертатися до UseStaticFiles?
MapStaticAssets — це майбутнє. Якщо ви стартуєте новий проєкт на .NET 9 — завжди використовуйте його. Однак, є кілька специфічних сценаріїв, де вам все ж доведеться використовувати стару систему:
- Динамічні файли (User Uploads):
MapStaticAssetsзнає лише про ті файли, які були вwwwrootна момент компіляції додатка. Якщо ваш додаток дозволяє користувачеві завантажити фотографію аватарки (яка фізично зберігається на диску),MapStaticAssetsїї ніколи не знайде і віддасть 404 помилку. Для роздачі "живих" файлів, завантажених під час роботи, вам доведеться додатиapp.UseStaticFiles()(зазвичай з вказуванням іншої папки для даних користувачів). - Перегляд директорій (Directory Browsing): Наразі
MapStaticAssetsне підтримує красивий перегляд списку файлів у системі.
var app = WebApplication.Create();
// Для наших системних стилів та JS, згенерованих під час білду (Швидко!)
app.MapStaticAssets();
// Для аватарів користувачів, завантажених під час роботи (Динамічно!)
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(Path.Combine(builder.Environment.ContentRootPath, "UserUploads")),
RequestPath = "/avatars"
});
app.Run();
Практичні завдання
UseStaticFiles() та MapStaticAssets(). Сфокусуйте вашу увагу на тому, коли (в який момент часу) сервер дізнається інформацію про вміст файлу в першому і другому випадках.Network (Мережа). Зайдіть на будь-який сучасний сайт (наприклад, YouTube або GitHub).
Знайдіть запит на завантаження .js або .css файлу. Перегляньте Request Headers (Заголовки запиту) і знайдіть поле Accept-Encoding. Що туди передає ваш браузер?
Потім подивіться на Response Headers (Заголовки відповіді сервера). Відшукайте поле Content-Encoding. Який алгоритм використав сервер, щоб надіслати цей конкретний файл вам?Статичні файли в ASP.NET Core
Будь-який сучасний веб-додаток, окрім динамічних даних (JSON з бази даних), потребує віддавати клієнту так звані статичні файли. Це HTML-сторінки, CSS-стилі, JavaScript-скрипти, зображення (PNG, JPG) та шрифти, які лежать на диску сервера і не змінюються при кожному запиті.
Конфігурація в ASP.NET Core: Основи
Будь-якому додатку потрібні налаштування. Рядки підключення до бази даних (Connection Strings), секретні ключі для JWT-токенів, адреси сторонніх API, таймаути чи навіть просто увімкнення режиму "технічних робіт" — усе це не можна тримати "захардкодженим" (hardcoded) прямо в C#.