- Обобщенная модель работы веб-сервера.
- HTTP запросы. Структура, заголовки.
- HTTP ответ. Структура, коды ответов, заголовки.
- Content-Type и Disposition.
- HTML формы в рамках протокола HTTP.
- URL.
- MIME.
- HTTP/2.
- Недостатки HTTP протокола.
- Cookies.
- HTTP Сессии.
- MVC. Схема паттерна, описание основных компонентов.
- Контроллер в MVC. Задачи и Реализация.
- MVC в web-приложениях.
- ASP.NET CORE. История и особенности.
- MVC и ASP.NET Core.
- Контроллеры и ASP.NET Core.
- Возвращаемое значение в контроллерах.
- Маршрутизация в ASP.NET Core MVC.
- Конфигурация статических файлов в ASP.NET Core MVC
- HTML-теги. Примеры.
- HTML формы (). Атрибуты, валидация.
- HTML таблицы.
- View Engine в ASP.NET Core MVC.
- Layout, _ViewStart.cshtml, _ViewImports.cshtml, Секции.
- HTML Helpers и Tag Helpers.
- Жизненный цикл запроса.
- Конфигурация ASP.NET Core.
- Среды развертывания.
- Обработка ошибок в ASP.NET Core.
- Middleware. Встроенные в ASP.NET Core.
- Фильтры в ASP.NET Core.
- Реализация фильтров-сервисов.
- Привязка моделей в ASP.NET Core.
- Валидация моделей в ASP.NET Core.
- JSON.
- XML.
- ASP.NET Core Web API.
- Возвращаемые значения в Web API.
- Web API – примеры методов в рамках HTTP запросов.
- Сервисной слой.
- DTO.
- DI контейнер, сервис и контроллер.
- Cross-Site Request Forgery.
- Аутентификация и Авторизация. Концепция и разница.
- ASP.NET Identity – основные компоненты для реализации собственной авторизации.
GET /index.html HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0
Accept: text/html
<CRLF>
-
Первая строка всегда состоит из
- Типа запроса (GET, POST, PUT, DELETE)
- URI Ресурса (./index.html)
- Версии протокола (HTTP/1.1)
-
Все последующие строки содержат HTTP заголовки
- User-Agent: Mozilla/5.0
-
Запрос заканчивается переводом строки
<CRLF>
HTTP/1.1 200 OK
Server: nginx/0.6.31
Content-Language: ru
Content-Type: text/html; charset=utf-8
<CRLF>
SOME AWESOME RESULT
- Первая строка всегда содержит
- Версию протокола (HTTP/1.1)
- Код состояния (200)
- Статус (OK)
- Все последующие строки содержат HTTP заголовки ответа
- Content-Language: ru
- Затем ответ содержит символ перевода строки
<CRLF>
- После него идут сами данные ответа (SOME AWESOME RESULT)
Заголовок Content-Type определяет тип передаваемого ресурса
Пример
Content-Type: text/html; charset=utf8
Заголовок Content-Disposition содержит информацию, как необходимо обрабатывать передаваемый ресурс
Пример:
Content-Disposition: attachment; filename="index.html"
- attachment - скачать
- inline - показать данные как есть
HTTP форма - раздел HTML документа, позволяющий пользователю вводить информацию для последующей обработки системой. Пример
<form action="index.php" method="post">
<label>
<input>
</form>
Заголовок формы содержит
- Атрибут action, который указывает, куда отправить данные
- Атрибут method, который указывает, каким типом запроса отправлять данные
Форма может содержать неограниченное количество input полей
NOTE: GET запрос ограничен 4КБ, так что большое количество текста может сломать логику вашей формы
Uniform Resource Locator
Пример
https://mysite.com:443/demo/index.php?id=27&lang=en#lectures
URL содержит
- Протокол для связи (http, https - чаще всего, ftp, magnet)
- Хост или IP адрес (example.com, 192.168.1.1)
- Порт подключения в диапазоне [0.65535] (http 80, http 443, ftp 21)
- Путь к ресурсу (demo/index.php)
- Строку запроса (пары ключ=значение, разделённые
&
)(?id=27&lang=en) - Фрагмен (якорь) для перехода к определённому разделу страницы (#lectures)
URL кодируется стандартом RFC 1738
- Поддерживаются любые незарезервированные символы
- Все зарезервированные или нестандартные символы экранируются в формате
%[code]
- например пробел
%20
- например пробел
Multipurpose Internet Mail Extensions
Спецификация для передачи по интернету медиа контента
Концепция MIME: То же самое, что в пункте 4
Заголовок Content-Type определяет тип передаваемого ресурса
Пример
Content-Type: text/html; charset=utf8
Заголовок Content-Disposition содержит информацию, как необходимо обрабатывать передаваемый ресурс
Пример
Content-Disposition: attachment; filename="index.html"
- attachment - скачать
- inline - показать данные как есть
HTTP/2 - вторая версия протокола HTTP, используемая в современном интернете
Главные отличия от HTTP/1
- Протокол является бинарным, а не текстовым
- Сервер теперь может послать незапрошенные данные, за счёт чего можно приклеивать к странице связанные файлы сразу (.css, .js)
- Сжатие заголовов и данных для ускорения запросов
- Мультиплексирование (склеивание) запросов (после запроса ресурса соединение не закрывается, а используется повторно)
Имеет полную обратную совместимость с HTTP/1
HTTP протокол не имеет состояния и не хранит информацию о запросах (stateless)
- Не поддерживает состояние, каждый запрос - отдельный и не связанный с остальными
- Сервер не знает, поступают ли 2 запроса от одного клиента или разных
- Проблема состояния - переход между страницами требует повторной аутентификации
- Информация теряется при переходе на другие страницы
- Сложная персонализация контента
Небольшой текстовый файл без кода
- Отправляется сервером при первом запросе
Set-Cookie: lang=en
- Хранится на клиенте
- При необходимости изменений, конкретный параметр задаётся в заголовке
Cookie: lang=ru
Зачем нужен:
- Корзины
- Логины
- Покупки
- История посещений
- Персонализация
- Отслеживание
Структура Cookie:
Набор пар ключ=значение, разделённые ;
Например: Set-Cookie: lang=en; time=22.00
Область действия Cookie определяется атрибутом Domain=page.html
При этом Path=/;
указывает на URL, при котором Cookie передаётся
Жизненный цикл Cookie
- Либо атрибут
Expires=Wed 13 Jan 2021 22:00:00 GMT
- дата окончания действия - Либо атрибут
Max-Age=100
, время в секундах от момента получения (0 - Cookie не используется)
Способ хранения информации о пользователе при использовании нескольких страниц
При использовании сессии пользователь может быть однозначно идентифицирован между несколькими запросами
Суть заключается в том, что клиент сохраняет ID сессии в локальном хранилище (например в Cookie) и при необходимости передаёт серверу
Model-View-Controller - архитектурный паттерн, разработанный Трюгве Реенскаугом в 70-х в языке SmallTalk
Концепция разделения компонентов кода по назначению с целью переиспользования
Был популярен в Desktop приложениях, однако сейчас в основном заменён на родственные
MVP (Model-View-Presenter) и MVVM (Model-View-ViewModel)
Компоненты
- Model - набор классов, описывающих предметную область (часто, прямые объекты из БД)
- View - набор классов, определяющих, как и что будет отображаться на экране
- Controller - набор классов, отвечающих за обработку запросов пользователя и выдачую данных во View
Все запросы пользователя идут в соответствующий контроллер, который
Обращается в модель за данными и передаёт их по View для формирования интерфейса
Содержит всю логику уровня приложения (всё, что не связано с моделью)
Пример:
- Пользователь нажимает на кнопочку
Узнать %DATA_NAME%
(подставьте любой объект) - Кнопочка сообщает контроллеру, что она была нажата
- Контроллер обрабатывает сообщение - обращается к модели и запрашивает данные
- Контроллер может обратиться к View для запроса изменений (например заблокировать кнопочку)
- Модель оповещает представление об изменении состояния (например переключилась песенка)
- Представление запрашивает у модели информацию состояния
Контроллер не ограничен передачей данных!
Он интерпретирует ввод данных и выполняет соответствующие действия с моделью
- Входящий запрос от браузера отправляется в контроллер
- Контроллер обрабатывает запрос и создаёт модель представления (ViewModel) + выбирает View
- ViewModel передаётся во View
- View преобразует ViewModel в HTML и возвращает HTTP Response
ASP.NET CORE - кроссплатформенный фреймворк для создания веб приложений (веб-сайты, сервисы, приложения-сервисы)
Фреймворк предлагает множество технологий: MVC, WebAPI, ViewEngine (Razor)
Может быть использован с фронтенд решениями: React, Angular, Blazor
ASP.NET CORE может быть запущен как на старом .Net Framework, так и на .Net Core
ASP.NET появился в 2002 году, работал только на Windows в среде IIS
С появлением .Net Core, появился ASP.NET Core - 2016, кроссплатформенный и улучшенный
Сейчас уже появился .Net 5, предлагающий лучшие скорости работы, унифицированные инструменты разработки и C# 9
ASP.NET Core MVC предоставляет функции для создания WebAPI и веб-приложений
Основные аспекты:
- Использует шаблон MVC
- Open-source
- Легковесный
- Разработчку нужно указать только нестандартные аспекты (соглашение важнее конфигураций)
- Автоматическая привязка моделей к данным HTTP запросов
- Поддержка валидаций
- Роутинг
- Razor
- Filter, Middleware и т.д.
- Контроллеры располагаются в папке Controllers
- Соглашение именования - %Name%Controller
- Каждый контроллер наследует класс Controller, который предоставляет доступ к Request, Response, HttpContext, RouteData, ModelState, User, ViewBag
- На основе маршрута выбирается контроллер
- Каждый контроллер содержит Action методы, каждый запрос привязывается к конкретному методу
Возвращаемое значение представляет собой различные коды HTTP статусов
Тип возвращаемого значения унаследован от ActionResult
Пример:
- StatusCodeResult -
return Ok("Success");
, - JsonResult -
return Json(new object());
, - ContentResult -
return Content(AppVersion);
, - FileContentResult -
return File(fileStream, mimeType, fileName);
ASP.NET Core сопоставляет параметры методов по шаблонам (например Users/{id})
За маршрутизацию отвечает Middleware
Маршруты с помощью шаблонов описывает Mapping запроса на конкретный Action у контроллера
- По соглашению - не требуется дополнительная конфигурация, достаточно основной
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseEndpoints(endpoints => {
endpoints.MapControllerRoute(name: "default", template: "{controller=Home}/{action=Index}/{id?}");
});
}
Маршруты поддерживают RegEx:
endpoints.MapControllerRoute(name: "blog", template: "{year}/{month}/{day}",
defaults: new { controller="Blog", action= "ByDate" },
constraints: new { year=@"\d{4}", month=@"\d{1,2}", day=@"\d{1,2}",
});
- На основе аттрибутов методов контроллера (HttpPost, Action, Route)
public class ArticlesController : Controller
{
[Route("/blog/{page}")]
[HttpPost]
public IActionResult List(int page)
{
return View(page);
}
}
Статические файлы необходимы для работы веб-приложения (фронтенд)
.html, .css, .js и другие ресурсы могут быть переданы приложению с помощью ASP.NET Core
Примеры:
- Хостинг стандартной папки wwwroot
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseStaticFiles();
}
- Хостинг кастомной папки
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseStaticFiles(); // для стандартной папки wwwroot
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "OtherFiles")),
RequestPath = new PathString("/files")
});
}
Hyper Text Markup Language
HTML - стандартизированный язык разметки, код интерпретируется браузерами
Для оформления страниц используют каскадные таблицы стилей (CSS)
Актуальная версия - HTML5
Семантическая вёрстка - подход к созданию HTML страниц с использованием HTML тегов на основе их семантики
Семантический элемент чётко описывает его значение как для браузера так и для разработчика
<p>Some random text...</p>
Теги позволяют браузеру понять, что из себя представляет страница..
Пример:
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<title>TITLE</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
...
</body>
</html>
<header>
- вводный контент, содержит заголовок и нередко - логотип<nav>
- отдельная секция документа<body>
- весь контент страницы, может быть только 1<main>
- основная часть контента внутри<body>
<title>
- заголовок страницы, который отображается во вкладке браузера<aside>
- часть документа, которая не связана с основным контентом сайта (чаще боковые панели, сноски)<footer>
- содержит нижний колонтитул (футер, подвал) страницы<section>
- представляет раздел страницы<article>
- независимый раздел страницы, предназначенный для распространения<figure>
- самостоятельный контент с заголовком<details>
- раскрытие дополнительной информации, если в самом начале есть<summary>
, то это метка виджета раскрытия<time>
- представление времени в 24-х часовом формате, либо точной даты<address>
- контактные данные для ближайшего<article>
или<body>
HTML формы содержат интерактивные элементы управления, которые позволяют отправлять информацию на сервер
Пример:
<form>
<label for="fname">First name:</label><br>
<input type="text" id="fname" name="fname" value="John"><br>
<input type="submit" value="Submit">
</form>
<input type="text">
<input type="number">
<input type="password">
<input type="email">
<input type="search">
<input type="checkbox">
<input type="radio">
<input type="range">
<input type="submit">
<input type="button">
<input type="file">
Атрибуты:
value
- начальное значение у элемента, необязателен для всех, кромеradio
иcheckbox
name
- имя элемента при отправкеplaceholder
- подсказка пользователю, работает только вtext
,search
,tel
,url
илиemail
required
- поле обязательно к заполнению перед отправкойautofocus
- элемент должен получить фокус при загрузке страницыdisabled
- элемент недоступен для взаимодействияmin
иmax
- минимальное и максимальное значения соответственно
Валидация:
- Производится автоматически браузером на основе спец атрибутов
- Выполняется только при отправке формы
- Отключенные или доступные только для чтения элементы не проверяются
- Таблица
<table>
- Каждая строка -
<tr>
- Каждая ячейка -
<td>
или<th>
Пример:
<table>
<tr>
<td>Cell 1.1</td>
</tr>
<tr>
<td>Cell 2.1</td>
</tr>
</table>
У каждой таблицы есть
- Заголовок
- Тело
- Подвал
(не более 1) - Для хранения одной или нескольких строк
<thead>
вверху таблицы<tbody>
в середине таблицы<tfoot>
внизу таблицы
- Views в ASP.NET Core MVC используют движок Razor View Engine для встраивания кода .NET в HTML-разметку.
- Содержат минимальную логику, только для отображения данных
- Данные могут быть переданы через ViewData, ViewBag или ViewModel
- ViewBag (dynamic тип):
- Action:
ViewBag.Message = "Hello World!";
- View:
@ViewBag.Message
- Action:
- ViewData (dictionary)
- Action:
ViewData["message"] = "Hello World!";
- View:
@ViewData["message"]
- Action:
- Подготовленные модели (строгая типизация):
- Action:
return View(model);
- View:
@model ModelDataType и затем @Model.<Свойство>
- Action:
- ViewBag (dynamic тип):
Razor использует специальные языковые вставки, обычно начинающиеся с @
@if
- условия@* *@
- комментарии@(...)
- выражения@using, @model
- подключения
Layout - определяет основной шаблон сайта
Находится в файле
~/Views/Shared/_Layout.cshtml
- Как правило View ссылается на подобный шаблон и по сути является просто содержимым шаблона
- Razor вначале рендерит саму View, а затем рендерит шаблон и вставляет в него готовый View
- В шаблоне указывается место куда подсунется готовое View
@RenderBody()
_ViewStart.cshtml
- По умолчанию, View нет необходимости указывать что они привязаны к шаблону, так как шаблон по умолчанию указывается в
~/Views/_ViewStart.cshtml
- Если нужно выбрать другой шаблон для View, укажите его во View
@{ Layout = "~/Views/Shared/_UncommonLayout.cshtml"; }
_ViewImports.cshtml
- Если директива или зависимость являются общими для многих представлений они могут быть указаны глобально в ViewImports:
~/Views/_ViewImports.cshtml
- Данный файл не поддерживает остальные возможности Razor
Razor позволяет создавать Cекции
- Определяются во View
@section SideBar { <aside> Some side info </aside> }
- Секции рендерятся в шаблоне используя
RenderSection()
- Используется как правило для «передачи» HTML кода шаблону
- Razor предоставляет удобный механизм рендеринга HTML тегов
- Через свойство Html (Каждая RazorPage имеет данное свойство)
- Но такой подход не рекомендуется, лучше используйте Tag Helpers!
Примеры HTML Helpers:
@Html.ActionLink
@Html.TextBox
@Html.BeginForm
@Html.TextArea
@Html.CheckBox
@Html.Password
@Html.Display
@Html.Hidden
@Html.Editor
@Html.Label
@Html.DropDownList
@Html.Action
Tag Helpers позволяют участвовать серверному коду в создании и рендеринге HTML-элементов в Razor представлениях
- Существуют встроенные Tag Helpers для многих распространенных задач
- Tag Helpers предоставляют
- Близкий к HTML опыт разработки
- Отличная поддержка IntelliSense для создания Razor разметки
- Более производительный, надежный и поддерживаемый код
- Можно создать свой Tag Helper
[HtmlTargetElement("h1")] public class HelloTagHelper : TagHelper { private const string MessageFormat = "Hello, {0}"; public string TargetName { get; set; } public override void Process(TagHelperContext context, TagHelperOutput output) { string formattedMessage = string.Format(MessageFormat, this.TargetName); output.Content.SetContent(formattedMessage); } }
@using WebApplication; @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelper @addTagHelper WebApplication.TagHelpersHelloTagHelper, WebApplication <div class="tag-helper-content"> <h1 target-name="John"></h1> </div>
ASP.NET Core использует метод Configure() в файле Startup.cs для:
- Настройки конвейера HTTP-запросов
- Определение поведения для различных сред развертывания
- Это делается с помощью IApplicationBuilder и IHostingEnvironment
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); }
else { app.UseExceptionHandler("/Home/Error"); }
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseMvcWithDefaultRoute();
}
-
Конфигурация приложения в ASP.NET Core основана на парах ключ-значение
- Конфигурации приложения задаются в провайдерах конфигураций
- Провайдеры конфигураций считывают данные из специальных источников конфигураций
- Хранилище ключей Azure, аргументы командной строки
- Пользовательские провайдеры (установленные или созданные)
- Специальные файлы, переменные окружения и т. д.
- Одним из источников по умолчанию является
appsettings.json
-
Конфигурация приложения считывается при запуске приложения с помощью провайдеров конфигурации
- Конфигурационные свойства мэпятся в IConfiguration
- IConfiguration доступен в DI контейнере приложения
-
Конфигурационные параметры, по соглашению, задаются в
ConfigureServices()
- Вызывается перед методом
Configure()
объектом WebHost - Как правило все вызовы по шаблону
add{Service}
, а затемservices.Configure(Service)
- Добавление сервиса в контейнер сервисов:
- Делает их доступными в приложении
- Доступ реализуется через внедрение зависимости (Dependency Injection)
- Делает их доступными в методе
Configure()
- Через
IApplicationBuilder.ApplicationServices
- Вызывается перед методом
Пример конфигурации сервисов:
public void ConfigureServices(IServiceCollection services)
{
// Transient objects are always different
// A new instance is provided to every controller and service
services.AddTransient<DataService>();
// Scoped objects are the same within a request
// They are different across different requests
services.AddScoped(typeof(DataService));
// Singleton objects are the same for every object and request.
services.AddSingleton<DataService>();
...
}
-
Развертывание программного обеспечения обычно производится в нескольких средах
- Многоэтапное развертывание является обязательным при разработке корпоративных приложений
- ОС (реальная или виртуальная) с необходимой средой для приложения, которая запускает ваше программное обеспечение
- Может включать в себя задачи, которые выполняются в программном приложении (например, модульные или интеграционные тесты)
-
Виды сред развертывания (часто встречаемые):
- Dev – среда, где разрабатывается программное обеспечение
- Test – где ПО тестируется и проверяется разработчиками
- Stage – среда приближенная к Production, закрытая для пользователей
- Production – когда продукт становится доступным для всех пользователей
-
ASP.NET Core настраивает поведение приложения на основе среды выполнения
- Фреймворк поддерживает 3 среды – Development, Staging и Production
- ASP.NET Core считывает переменную окружения –
ASPNETCORE_ENVIRONMENT
- Значение хранится в IHostingEnvironment.EnvironmentName
- Вы можете установить любое значение. Но, среда по умолчанию - Production
-
Есть несколько путей конфигурации обработки ошибок в ASP.NET Core:
- Developer Exception Page – страница ошибки для разработчика
- Exception Handler – обработчик ошибок
- Status Code Pages – страница кода ошибки/статуса запроса
- Приложения ASP.NET Core не имеют по умолчанию расширенной страницы с кодом или статусом
- Используется Status Code Pages Middleware
- app.UseStatusCodePages()
- Данный middleware легко кастомизируются
- В частности поддерживают extension methods
- app.UseStatusCodePagesWithRedirects(…)
- app.UseStatusCodePagesWithReExecute(…)
- Приложения ASP.NET Core не имеют по умолчанию расширенной страницы с кодом или статусом
- Приложения ASP.NET Core MVC дополняются способами:
- Exception Filters – фильтры ошибок
- Model Validation (ModelState) – валидация моделей
-
Настройка кастомной страницы для ошибки используя ExceptionHandlerMiddleware
-
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseExceptionHandler("/Home/Error"); }
- Вы можете реализовать обработчик по данному адресу
- Это может как Action контроллера, страница Razor или другой обработчик
-
Middleware - это ни что иное, как компонент (класс), который выполняется при каждом запросе в приложении ASP.NET Core.
Каждый компонент:
- Обрабатывает запросы и ответы
- Определяет, следует ли передавать запрос следующему компоненту конвейера
- Может выполнять работу до или после вызова следующего компонента в конвейере
Request Delegates обрабатывают каждый HTTP запрос
-
Настраиваются с помощью extension methods
Run()
,Map()
иUse()
-
Request Delegates (также называемые компонентами middleware) могут быть:
- анонимный метод (in-line middleware)
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.Use(async (context, next) => { // Do work that doesn't write to the Response. await next(); // Do logging or other work that doesn't write to the Response. }); }
- определен как класс (для многократного повторного использования)
public class CustomMiddleware { private readonly RequestDelegate next; public CustomMiddleware(RequestDelegate next) { this.next = next; } // IMyService is injected into InvokeAsync public async Task InvokeAsync(HttpContext httpContext, IMyService svc) { svc.MyProperty = 1000; await this.next(httpContext); } }
public static class CustomMiddlewareExtensions { public static IApplicationBuilder UseCustom(this IApplicationBuilder builder) { return builder.UseMiddleware<CustomMiddleware>(); } }
public class Startup { public void Configure(IApplicationBuilder app) { app.UseCustom(); ... } }
- анонимный метод (in-line middleware)
-
Каждый компонент middleware отвечает за:
- Вызов (Invoking) следующего компонента в конвейере
- short-circuiting (замыкает) the pipeline – завершает обработку запроса в конвейере
-
Метод
Use()
используется для объединения нескольких делегатов в одну цепочку- Метод может закоротить конвейер (если он не вызывает
next()
)
- Метод может закоротить конвейер (если он не вызывает
-
Первый
Run()
делегат останавливает конвейерRun()
- это соглашение- Некоторые компоненты middleware могут предоставлять
Run{Middleware}
методы - Эти методы выполняются в конце конвейера
-
Метод
Map()
используется для ветвления конвейера- Конвейер запросов разветвляется на основе заданного URL запроса
- Конвейер запросов ASP.NET Core состоит из последовательности Request Delegates, вызываемых один за другим
- Пользовательские Request Delegates создаются с помощью IApplicationBuilder
Встроенные Middleware:
- UseAuthentication
- UseCookiePolicy
- UseCors
- Diagnostics
- UseExceptionHandler
- UseDevelopmentExceptionPage
- UseStatusCodePages
- UseHttpsRedirection
- UseHsts
- UseStaticFiles
- UseResponseCaching
- UseResponseCompression
- UseRequestLocalization
- UseRouter
- UseSession
- UseRewriter
- UseWebSockets
- UseWelcomePage
Фильтры позволяют запускать код до или после определенных этапов в конвейере обработки запросов
- Фильтры похожи, но на то же самое, что middleware
- Middleware работает на уровне ASP.NET Core
- Фильтры работают только на уровне MVC
Существуют несколько видов фильтров
- Authorization Запускается первым. Определяет авторизован ли пользователь для доступа к ресурсу данного запроса
- Resource Запускается сразу за Authorization. Может запускать код перед или после всего в конвейере.
- Action Запускается сразу перед и после вызова соответствующего Action метода
- Exception Добавляет глобальные политики для возникающих необработанных ошибок
- Result Запускается сразу перед и после вызова соответствующего Action Result метода
Создание фильтра:
public class SampleActionFilter : IActionFilter
{
public void OnActionExecuting(ActionExecutingContext context)
{
//Код – перед действием
}
public void OnActionExecuted(ActionExecutedContext context)
{
//Код – после действия
}
}
Фильтры добавляются в ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews(options =>
{
options.Filters.Add(typeof(SampleActionFilter));
});
}
public class FeatureAuthFilter : IAuthorizationFilter
{
IFeatureService featureAuth;
public FeatureAuthFilter(IFeatureService service)
{
this.featureAuth = service;
}
}
services.AddSingleton<IFeatureService, FeatureService>();
services.AddSingleton<FeatureFilter>();
[TypeFilter(typeof(FeatureAuthFilter))]
public IActionResult Index()
{
return View();
}
Привязка моделей в ASP.NET Core MVC обеспечивает сопоставление данных в HTTP запросах и параметров методов контроллера
- Параметрами могут быть как примитивные типы, так и пользовательские
- Сопоставление производится по имени
- В случае объектов классов сопоставление производится по именам публичный свойств
Пример:
https://mysite.com/posts/edit/6
-> PostsController public IActionResult Edit(int? id)
Привязка моделей работает с данными:
- Данные форм, отправленные в POST
- Данные в URL
- Данные в заголовках, куках, сессиях и т.д. с помощью кастомных привязок
- Данные с данных источников хранятся парами имязначение
- Фреймворк смотрит на параметры и ищет их в запросе, если не находит, идет смотреть следующий
- Порядок просмотра источников данных указан выше
Если привязка завершается неудачно, фреймворк не выдает ошибку
- Каждый action метод, принимающий пользовательский ввод, должен проверять, была ли привязка успешной
- Результат можно узнать через свойство ModelState.IsValid
- ModelState также содержит коллекцию ошибок
- Привязка моделей по умолчанию отлично подходит для большинства сценариев разработки
- Тем не менее все кастомизируется
Пройдёмся по списку ошибок:
public class UsersController : Controller
{
public IActionResult Register(RegisterUserBindingModel model)
{
if(!ModelState.IsValid)
{
foreach (var error in ModelState.Values.SelectMany(v => v.Errors))
{
DoSomething(error);
}
// TODO: Return Error Page
}
return Ok("Success!");
}
}
Фреймворк позволяет указать поведение привязки
- [BindRequired] Добавляет model state ошибку если привязка не удалась.
- [BindNever] Указываем, что данный параметр нужно игнорировать при привязке.
- [From{source}] Используется для указания источника данных для привязки. [FromHeader], [FromQuery], [FromRoute], [FromForm], [FromBody]
- [FromServices] Используется dependency injection для привязки параметров с сервисов.
- [ModelBinder] Используется чтобы указать какой класс реализует связывание, которое будет применено для выбранного параметра.
Кастомный Binder
-
Для создания необходимо создать – BindingProvider и Binder
[ModelBinder(BinderType = typeof(StudentEntityBinder))] public class Student { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } }
public class StudentEntityBinder : IModelBinder { public Task BindModelAsync(ModelBindingContext bindingContext) { //TODO: Do Magic ... bindingContext.Result = ModelBindingResult.Success(model); return Task.CompletedTask; } }
public class StudentEntityBinderProvider : IModelBinderProvider { public IModelBinder GetBinder(ModelBinderProviderContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } if (context.Metadata.ModelType == typeof(Student)) { return new BinderTypeModelBinder(typeof(StudentEntityBinder)); } return null; } }
-
И сконфигурировать приложение для работы с новым binder’ом (0 - вставить в начало)
public void ConfigureServices(IServiceCollection services) { services.AddControllers(options => { options.ModelBinderProviders.Insert(0, new StudentEntityBinderProvider()); }); }
В .NET представлена абстрактная модель валидации через атрибуты - Одни атрибуты как правило описывают ограничения значений * Аналогично как ограничения в базах данных - Другие применяют шаблоны к данным * Кредитные карты, номера телефонов, адреса электронной почты и т. д. - Атрибуты простой способ валидации * Они задаются на уровне свойств
Популярные встроенные атрибуты:
- [CreditCard] - строка соответствует кредитной карте
- [Compare] - проверка на равенство
- [EmailAddress] - строка соответствует email-адресу
- [Phone] - строка соответствует шаблону телефона
- [Range] - число в промежутке
- [RegularExpression] - строка соответствует указанному регулярному выражению
- [Required] - поле обязательно (не null)
- [StringLength] - проверка длины строки
- [Url] - строка соответсвует шаблону Url ссылки
Иногда нужно создавать кастомные атрибуты
public class IsBefore : ValidationAttribute
{
private const string DateTimeFormat = "dd/MM/yyyy";
private readonly DateTime date;
public IsBefore(string dateInput)
{
this.date = DateTime.ParseExact(dateInput, DateTimeFormat, CultureInfo.InvariantCulture);
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
if ((DateTime)value >= this.date) return new ValidationResult(this.ErrorMessage);
return ValidationResult.Success;
}
}
Использование:
public class RegisterUserModel
{
[Required]
public string Username { get; set; }
[Required]
[StringLength(20)]
public string Password { get; set; }
[IsBefore("01/01/2000")]
public DateTime BirthDate { get; set; }
}
Также можно валидировать прямо в Bind Model
- Реализуя IValidatableObject
-
public class RegisterUserModel : IValidatableObject { public string Username { get; set; } public string Password { get; set; } public string ConfirmPassword { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if(string.IsNullOrEmpty(Username)) yield return new ValidationResult("Username cannot be empty"); if(string.IsNullOrEmpty(Password)) yield return new ValidationResult("Password cannot be empty"); if(ConfirmPassword != Password) yield return new ValidationResult("Passwords do not match"); } }
JavaScript Object Notation (JSON) - это открытый файловый стандарт
- Применяется подход читабельного для человека текста для передачи объектов данных
- Объекты данных состоят из пар атрибут-значение или массива данных
- Формат прост как для людей, так и для автоматизированного анализа и генерации
У JSON истоки от JavaScript
- Однако он не зависит от какого-либо языка
- На многих языках есть инструменты для генерации и анализа JSON
{
"firstName": "Ivan",
"courses": ["C#", "ASP.NET"],
"age": 21,
"date": "2012-04-23T18:25:43.511Z"
}
JSON - это очень распространенный формат данных, используемый в веб-коммуникации
- В основном для связи браузер-сервер или сервер-сервер
- Официальный (тип медиа) MIME для JSON - это файлы JSON, использующие расширение .json
- JSON обычно используется в качестве замены XML
- JSON короче и проще для понимания
- JSON быстрее считывается и генерируется, а также более интуитивный
- JSON не поддерживает схемы и пространства имен
eXtensible Markup Language
- Расширяемый язык разметки
- Похож на JSON
- С точки зрения читабельности человеком и автоматизированным способом
- С точки зрения иерархии (значения внутри значений)
- XML - это текстовый формат данных
- Сильная поддержка различных языков используя Unicode
- Дизайн отражает необходимость представления реальных документов
Пример
<?xml version="1.0" encoding="UTF-8" ?>
<records>
<record id="1">
<name>Ivan</name>
<email>ivan@example.com</email>
<company>RUT</company>
</record>
<record id="2">
<name>Petr</name>
<email>petr@example.com</email>
<company>RUT</company>
</record>
</records>
XML Существует 2 типа MIME
- application/xml
- text/xml
XML широко применяется:
- В SOA(Сервис-ориентированная архитектура) подходе (например, WCF)
- Для настройки .NET приложений
- В форматах Microsoft Office
Web API - это интерфейс прикладного программирования
- Используется веб-браузером (SPA - одностраничное приложение), мобильными приложениями, играми, настольными приложениями, веб-сервером и т. д.
- Серверные веб-API (Server-Side Web APIs) состоят из общедоступных конечных точек (endpoints) (URL-ов)
- Данные точки предоставляют систему связи запрос-ответ
- Общение как правило происходит в формате JSON или XML
- Общение как правило происходит в рамках протокола
- Чаще всего HTTP
Создание веб-API с помощью ASP.NET довольно простое
- Нет ничего кардинально отличающегося от обычного вебприложения
- Создаются контроллеры, в них как и обычно описываются методы-действия
- Методы играют роль конечных точек
- Контроллер аннотируется как API Controller
[ApiController] [Route("api/[controller]")] public class WeatherForecastController : ControllerBase { … }
- Контроллер наследуется от ControllerBase
[Route("api/[controller]")] [ApiController] public class ProductController : ControllerBase { private readonly IProductService productService; public ProductController(IProductService productService) { this.productService = productService; } }
- Контроллер аннотируется [ApiController] и [Route] атрибутами
- Аннотация [ApiController] предоставляет несколько удобных функций
- Автоматические HTTP 400 ответы (для ошибок model state)
- Привязка моделей из разных источников
- Поддержка multipart/form-data
- Поддержка глобального пути
- Подробные сведения о ошибках, коды состояния ошибок
ASP.NET Core предлагает несколько опций для возвращаемых значений:
- Конкретный тип-значение
- IActionResult Type
- Используется когда возможны разные варианты ответа
[HttpGet("{id}")] [ProducesResponseType(200, Type = typeof(Product))] [ProducesResponseType(404)] public IActionResult GetById(int id) { var product = this.productService.GetById(id); if (product == null) return NotFound(); return Ok(product); }
- Рекомендуется использовать ActionResult
[HttpGet("{id}")] [ProducesResponseType(200)] [ProducesResponseType(404)] public ActionResult<Product> GetById(int id) { var product = this.productService.GetById(id); if (product == null) return NotFound(); return product; }
- Создание GET методов для WebApi:
[HttpGet] // GET: /api/product public ActionResult<IEnumerable<Product>> GetProducts() { return this.productService.GetAllProducts(); }
[HttpGet("{id}")] // GET: /api/product/5 public ActionResult<Product> GetProduct(long id) { var product = this.productService.GetById(id); if (product == null) return NotFound(); return product; }
- Создание POST методов для WebApi:
CreatedAtAction * Возвращает HTTP 201 (Created) * Добавляет Location в заголовки * Использует путь GetProduct для генерации URL
[HttpPost] // POST: api/product public ActionResult<Product> PostProduct(ProductBindingModel productModel) { this.productService.RegisterProduct(productModel); return CreatedAtAction("GetProduct", new { id = productModel.Id }, productModel); }
- Создание PUT методов для WebApi:
[HttpPut("{id}")] // PUT: api/product/5 public IActionResult PutProduct(long id, ProductBindingModel productModel) { if (id != productModel.Id) return BadRequest(); this.productService.EditProduct(id, productModel); return NoContent(); }
- Используется как Update – обновление существующей сущности (HTTP PUT)
- Ответ - 204 (No Content)
- Создание DELETE методов для WebApi:
[HttpDelete("{id}")] // DELETE: api/product/5 public ActionResult<Product> DeleteProduct(long id) { var product = this.productService.DeleteProduct(id); if (product == null) return NotFound(); return product; }
- HTTP DELETE
- Ответ - 204 (No Content)
- Сервисной слой представляет собой «фасад», скрывающий логику работы с данными от контроллера
- Сервисный слой является частью Model
- Сервисный слой работает с Model, то есть с моделями предметной области (вашим доменом)
- Контроллер при этому теряет связь с моделью и доменом, общаясь через соответствующие сервисы
- Фасад – это паттерн ☺
Пример сервиса:
public interface IMovieService
{
MovieDto GetMovie(int id);
IEnumerable<MovieDto> GetAllMovies();
MovieDto UpdateMovie(MovieDto movieDto);
MovieDto AddMovie(MovieDto movieDto);
MovieDto DeleteMovie(int id);
}
- Сервисный уровень возвращает и получает DTO
- По сути всю логику с доменом сервисы инкапсулируют, тем самым слой контроллеров и выше становится независимым от модели
- Модель может меняться, при этом DTO не затрагивается
- DTO и ViewModel близкие понятия, но ViewModel привязаны к интерфейсам графическим, а DTO абстрактные данные с привязкой к логике данных
public class MovieService:IMovieService
{
private readonly MoviesContext _context;
private readonly IMapper _mapper;
public MovieService(MoviesContext context, IMapper mapper)
{
_context = context;
_mapper = mapper;
}
//All methods…
}
public class MoviesApiController : ControllerBase
{
private readonly IMovieService _service;
public MoviesApiController(IMovieService service)
{
_service = service;
}
[HttpGet] // GET: /api/movies
[ProducesResponseType(200, Type = typeof(IEnumerable<MovieDto>))]
[ProducesResponseType(404)]
public ActionResult<IEnumerable<MovieDto>> GetMovies()
{
return Ok(_service.GetAllMovies());
}
//Other actions
}
public void ConfigureServices(IServiceCollection services)
{
//other code
services.AddScoped<IMovieService, MovieService>();
}
Cross-Site Request Forgery (CSRF / XSRF) – одна из видов атак через HTTP протокол
Выполнение запросов за пользователя
Используя украденный cookies, хранящийся в браузере
Защита от таких атак обязательна!
AutoValidateAntiforgeryToken
При использовании tag helper <form>
в ASP.NET Core,
- автоматически добавляется в форму специальное скрытое поле со случайным значением, называемым токеном защиты от подделки (anti-forgery token)
- Проверяйте наличие данного токена в POST методах:
[AutoValidateAntiforgeryToken] public IActionResult SendMoney(…) { … }
- Глобально в контроллерах или приложениях
[AutoValidateAntiforgeryToken] public class ManageController : Controller
services.AddMvc(options => options.Filters.Add(new AutoValidateAntiForgeryTokenAttribute()))
- Аутентификация
- Процесс проверки личности пользователя или компьютера
- Вопрос: Кто вы? Как вы это докажете?
- Учетные данные могут быть паролями, смарт-картами, токенами и т. д.
- Авторизация
- Процесс определения того, что пользователю разрешено делать
- Вопрос: Что вам разрешено делать? Вы можете увидить эту страницу?
ASP.NET Identity - Фреймворк в ASP.NET для реализации аутентификации иавторизации
- Поддерживается ASP.NET MVC, Pages, Web API (с помощью токенов)
- Управляет Users, User Profiles, Login / Logout, Roles
- Поддержка внешних провайдеров аутентификации и авторизации
- Facebook, Google, Twitter и т. д.
Используем пакет Microsoft.AspNetCore.Identity.EntityFrameworkCore 3.1.10
Реализация:
- ApplicationUser - модель пользовательских данных
public class ApplicationUser : IdentityUser { public string FirstName { get; set; } public string LastName { get; set; } }
- Обновляем контекст - Наследуем IdentityDbContext
public class MoviesContext: IdentityDbContext<ApplicationUser> { //... }
- Добавляем Identity для контекста в ConfigureServices
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<MoviesContext>();
-
Создаем миграцию и обновляем базу данных!
dotnet ef migration add %Migration_Name% dotnet ef database update
-
Добавляем Middleware
app.UseAuthentication(); app.UseAuthorization();
-
Добавляем в Seed пользователя и роль
if (!roleManager.RoleExistsAsync("Admin").Result) { roleManager.CreateAsync(new IdentityRole { Name = "Admin" }).Wait(); } if (userManager.FindByEmailAsync("admin@example.com").Result == null) { var user = new ApplicationUser { UserName = "admin@example.com", Email = "admin@example.com", FirstName = "Super", LastName = "Admin" }; IdentityResult result = userManager.CreateAsync(user, "P@ssw0rd").Result; if (result.Succeeded) { userManager.AddToRoleAsync(user, "Admin").Wait(); } }
-
Добавляем основной код
- Контроллеры
- AccountController
- ManageController
- Помечаем методы/контроллеры, к которым нужен авторизованный доступ
- К примеру - [Authorize(Roles = "Admin")]
- Создаем страницы и ViewModels для авторизации, управления аккаунтом
- Контроллеры
-
Настройки для пароля можно указать в ConfigureServices:
services.AddIdentity<ApplicationUser, IdentityRole>(options => {
options.Password.RequireNonAlphanumeric = false;
}).AddEntityFrameworkStores<MoviesContext>();
Используйте следующие атрибуты:
- [Authorize] – доступ только авторизованным пользователям
- [Authorize(Roles = "Admin")] – доступ только авторизованным пользователям с ролью «Admin»
- [AllowAnonymous] – доступ без ограничений
Интерфейс доступа к данным (репозиториям) – пользователям и ролям
- AddClaimsAsync(…)
- FindByEmailAsync(…)
- GenerateChangeEmailTokenAsync(…)
- AddToRoleAsync(…)
- FindByIdAsync(…)
- GenerateEmailConfirmationTokenAsync(…)
- IsInRoleAsync(…)
- FindByNameAsync(…)
- GeneratePasswordResetTokenAsync(…)
- GetUserId(…)
- GetClaimsAsync(…)
- GetAuthenticationTokenAsync(…)
- ConfirmEmailAsync(…)
- GetEmailAsync(…)
- IsEmailConfirmedAsync(…)
- ChangeEmailAsync(…)
- GetRolesAsync(…)
- CreateSecurityTokenAsync(…)
- CreateAsync(…)
- GetUserAsync(…)
- ResetPasswordAsync(…)
- DeleteAsync(…)
- CheckPasswordAsync(…)
- RemoveFromRoleAsync(…)
- Dispose(…)
- UpdateAsync(…)
- RemoveClaimsAsync(…)