.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 2176. #AI
Работа с LLM в .NET. Окончание
Начало

Резюмирование статьи
Попробуем что-то более полезное — выделение главной мысли (резюмирование) текста:
var posts = Directory.GetFiles("posts")
.Take(5).ToArray();
foreach (var post in posts)
{
var prompt = $$"""
You will receive an input text and the desired output format.
You need to analyze the text and produce the desired output format.
You are not allowed to change code, text, or other references.

# Desired response

Only provide a RFC8259 compliant JSON response following this format without deviation.

{
"title": "Title pulled from the front matter section",
"summary": "Summarize the article in no more than 100 words"
}

# Article content:

{{File.ReadAllText(post)}}
""";

var response =
await client.CompleteAsync(prompt);
Console.WriteLine(response.Message.Text);
Console.WriteLine(Environment.NewLine);
}

Совет: конкретизация выходного формата (например, запрос JSON, соответствующего RFC8259) помогает получать согласованные результаты.

Интеллектуальная категоризация
Мы можем получать строго типизированные ответы напрямую от LLM:
class PostCategory
{
public string Title { get; set; } = string.Empty;
public string[] Tags { get; set; } = [];
}

var posts = Directory.GetFiles("posts").Take(5).ToArray();
foreach (var post in posts)
{
//Такой же запрос, что и в предыдущем примере, только изменим формат JSON-ответа
var prompt = $$"""

{
"title": "Title pulled from the front matter section",
"tags": "Array of tags based on analyzing the article content. Tags should be lowercase."
}

""";

var response = await
client.CompleteAsync<PostCategory>(prompt);

Console.WriteLine(
$"{response.Result.Title}. Tags: {string.Join(",",response.Result.Tags)}");
}

Строго типизированный подход обеспечивает безопасность во время компиляции и лучшую поддержку IDE, что упрощает поддержку и рефакторинг кода, взаимодействующего с ответами LLM.

Использование различных поставщиков LLM
Одним из ключевых преимуществ Microsoft.Extensions.AI является поддержка различных поставщиков. Хотя в наших примерах используется Ollama, вы можете легко переключиться на других поставщиков:
// Azure OpenAI
builder.Services.AddChatClient(
new AzureOpenAIClient(
new Uri("AZURE_OPENAI_ENDPOINT"),
new DefaultAzureCredential())
.AsChatClient());

// Using OpenAI
builder.Services.AddChatClient(
new OpenAIClient("OPENAI_API_KEY")
.AsChatClient());


Это позволяет:
- Начать разработку с локальных моделей
- Развернуть приложение с облачными поставщиками
- Переключаться между поставщиками без изменения кода приложения
- Смешивать разных поставщиков для разных вариантов использования (категоризирование, распознавание изображений и т. д.)

Источник: https://www.milanjovanovic.tech/blog/working-with-llms-in-dotnet-using-microsoft-extensions-ai
👍11
День 2177. #ЗаметкиНаПолях
Быстрое и Безопасное Восстановление NuGet-пакетов
Некоторые параметры могут помочь повысить производительность и безопасность при восстановлении NuGet-пакетов.

Файлы блокировки
При восстановлении пакетов NuGet создает граф зависимостей, включающий все объявленные и транзитивные пакеты. Затем этот граф используется для определения того, какие пакеты следует загрузить и установить. Файл блокировки позволяет хранить граф зависимостей и повторно использовать его при восстановлении пакета. Это гарантирует, что разрешение всегда восстанавливает те же пакеты.

Преимущества:
- Безопасность: файл содержит хэш пакета. Если вы загружаете поврежденный или вредоносный пакет, NuGet может обнаружить его и отменить установку.
- Детерминированное восстановление: есть некоторые случаи, когда вычисление графа зависимостей не является детерминированным (разная конфигурация NuGet, плавающие версии, пакет удален на сервере и т. д.). Использование файла блокировки позволяет восстанавливать те же пакеты независимо от конфигурации.
- Производительность: не нужно снова вычислять график зависимости при восстановлении пакетов.
- Надёжность: при использовании сервера NuGet с поддержкой вышестоящего уровня, например Azure Artifacts или Artifactory, серверу не нужно связываться с вышестоящим сервером для вычисления графика зависимости. Таким образом, если nuget.org не работает, вы все равно можете восстановить пакеты, если они уже есть в кэше сервера.

Недостатки:
- Вы можете получить больше конфликтов слияния из-за файла блокировки.
- Некоторые инструменты, такие как Dependabot, не обновляют файл блокировки (на момент написания поста).

Чтобы включить файл блокировки, нужно добавить следующую строку в файл .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!--Генерация lock-файла -->
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>

<!-- Восстановление пакетов только из lock-файла -->
<RestoreLockedMode Condition="'$(ContinuousIntegrationBuild)' == 'true'">true</RestoreLockedMode>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="…" Version="…" />
</ItemGroup>
</Project>


Сопоставление источников пакетов
Если у вас несколько источников NuGet для восстановления пакетов, может потребоваться включить сопоставление источников пакетов. NuGet пытается загрузить пакеты из всех источников параллельно и получить результат из первого, кто ответит. Сопоставление источников пакетов позволяет указать, какой источник следует использовать для восстановления пакета.

Преимущества
- Детерминированное восстановление/безопасность: если несколько серверов содержат один и тот же пакет, его можно восстановить с любого из них. Но если пакеты отличаются, вы можете получить несогласованное восстановление.
- Производительность: NuGet не будет тратить время на запросы к нескольким серверам, поэтому вы сокращаете количество сетевых запросов на восстановление пакетов.

Чтобы использовать сопоставление источников пакетов, вам необходимо создать файл nuget.config в корне репозитория и адаптировать конфигурацию для каждого пакета:
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="mycompany.org" value="https://mycompany.org/nuget/" />
</packageSources>

<packageSourceMapping>
<!-- key должен соответствовать <packageSources> -->
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
<packageSource key="mycompany.org">
<package pattern="MyCompany.*" />
<package pattern="MyCompanySpecificPackage" />
</packageSource>
</packageSourceMapping>

См. также документацию по сопоставлению источников пакетов.

Источник: https://www.meziantou.net/faster-and-safer-nuget-restore-using-source-mapping-and-lock-files.htm
👍5
День 2178. #Оффтоп
Почему Открытый Код Важен и Бесплатный ли он? Начало
Какие из продуктов вы используете в своей личной или профессиональной работе: Serilog, Polly, PostgreSQL, Git, Curl, Jenkins, Blazor?

Скорее всего вы используете хотя бы один. Большинство ПО в той или иной степени построено на продуктах или библиотеках с открытым кодом. И часто они не поддерживаются большой компанией. Например, Log4J - самая известная библиотека логирования в экосистеме Java. Основная работа выполняется горсткой разработчиков без какой-либо финансовой поддержки. А она использовалась в Apple, Amazon, Steam, Alibaba и т.п.

Log4J / Log4Shell
Коротко напомню: специальный пользовательский ввод может привести к удалённому выполнению кода через сервер LDAP в контексте приложения. А удалённое выполнение кода – это большой красный флаг в сфере ПО. Проблема в том, как это было воспринято теми самыми компаниями, использовавшими Log4J. Согласно этой статье, мейнтейнеры библиотеки получили такое сообщение: «Мы обещаем сохранить это в тайне, пока не выйдет ваш официальный патч. Пожалуйста, поторопитесь.»

Понятно, что из-за такой уязвимости, возможно, придётся отключить затронутые сервисы. А для Amazon или Alibaba это может стоить миллионов и миллиардов дохода за очень короткое время. Мейнтейнеры написали в Твиттере о ситуации: «Сотрудники Log4j не покладая рук трудятся над мерами по смягчению последствий: исправлениями, документацией, CVE, ответами на запросы и т.д. Однако ничто не мешает людям критиковать нас за работу, за которую нам не платят, за функцию, которая нам всем не нравится, но которую необходимо сохранить из-за проблем с обратной совместимостью.»

Другие инциденты
LeftPad был пакетом NPM, который был удалён из-за спора между ментейнером, NPM и компанией. Суть: у мейнтейнера Azer был пакет под названием "kik". Также была компания "kik", которая хотела зарезервировать это имя для себя. Поскольку Azer уже занял это имя, были привлечены юристы. И NPM отдал имя компании. После этого Azer просто удалил свою наиболее используемую библиотеку "leftpad" (да, вы могли просто удалить пакеты из NPM). Конечно, если вы использовали "leftpad" напрямую или транзитивно, ваша сборка ломалась!

Moq — одна из самых известных библиотек в экосистеме dotnet. Создатель Дэниел Каццулино (более известный как kzu) попробовал альтернативный подход к монетизации: он разработал SponsorLink. По сути, в то время он проверял, сделал ли пользователь пожертвование на библиотеку, в которой используется SponsorLink. Он делал это, выполняя HTTP-запрос на какой-то сервер. Я описывал эту ситуацию подробно в этом посте.

Fluent Assertions — одна из популярнейших библиотек для утверждений в юнит-тестах начиная с версии 8 стала платной.

Эти инциденты вызвали довольно много негативной реакции, но все они показывают некоторые фундаментальные недостатки открытого кода:
- Проекты с открытым кодом считаются бесплатными.
- Монетизация — это сложно!
- Требуется поддержка проекта в рабочем состоянии в течение длительного периода
времени.

Продолжение следует…

Источник:
https://steven-giesel.com/blogPost/591f2354-a205-4507-bbb2-7d88781e0563/why-is-open-source-important-and-is-it-free
👍19👎1
День 2179. #Оффтоп
Почему Открытый Код Важен? И Бесплатный ли он? Продолжение

Начало

Открытый код бесплатный?
Простой ответ: Да. Можно же просто скачать любой пакет и использовать его? Ну… и да, и нет. Конечно, это «бесплатно». В том смысле, что вы не платите за труд по написанию кода, но есть другие затраты: обслуживание и зависимости. Это звучит странно. Разве не поэтому люди используют библиотеки других людей - чтобы меньше обслуживать код?

Плата за обслуживание
Когда вы используете открытый код, вы наследуете не только его функции, но и его жизненный цикл. Библиотеки с открытым кодом со временем развиваются. У них могут быть критические проблемы, из-за которых вам придётся обновлять пакет. Критические изменения или другие вещи, из-за которых придётся проверять, всё ли ваше ПО по-прежнему делает то, что оно должно делать.

Некоторые проекты с открытым кодом со временем теряют активных мейнтейнеров. Если вы полагаетесь на такую библиотеку, у вас остаётся три варианта:
- Продолжить использовать устаревшую версию (риск безопасности).
- Перейти на альтернативу (может потребовать много усилий, не предоставив прямой ценности).
- Взять на себя обслуживание (сделать форк). Это затраты на обслуживания кода, который вам нужен, а может и не нужен или нужен не весь. Иногда библиотека, от которой вы зависите, не соответствует вашим потребностям, поэтому вам, возможно, придется её форкнуть. Теперь вы также «владеете» кодом со всеми его затратами, зависимостями и последствиями.

Во всех этих случаях вы платите, тратите время и деньги. Каждая зависимость, которую вы создаёте, будет иметь цену. Вам нужно взвесить, оправдана ли она. И эти затраты будут возникать время от времени, а не один раз за весь срок существования проекта. Это особенно важно, если вы используете много микробиблиотек, вроде Left Pad, которые чаще всего содержат не более 10 строк кода и выполняют элементарные функции. Еще один отличный пример: is-even и is-odd. У обеих до сих пор 200000 (да, двести тысяч) загрузок в неделю.

Так что нет, открытый код не бесплатный. Он бесплатный в том смысле, что не стоит денег, но почти всё имеет свою цену! Конечно, то же самое относится и к библиотекам с закрытым кодом, которые вы можете использовать. Они также стоят денег, но они стоят вам большую часть времени денег сверху, и вы не можете наблюдать за процессом разработки открыто.

Все инциденты, описанные ранее, известны и были быстро устранены, потому что всё происходило открыто и прозрачно. Это хорошо. Этим открытый код и прекрасен. И в заключении мы поговорим о другой стороне – мейнтейнерах.

Окончание следует…

Источник:
https://steven-giesel.com/blogPost/591f2354-a205-4507-bbb2-7d88781e0563/why-is-open-source-important-and-is-it-free
👍7
Если у вас стандартный 8-часовой рабочий день, сколько в среднем времени вы реально занимаетесь рабочими задачами?
Anonymous Poll
23%
не больше 4 часов
29%
4-5 часов
20%
5-6 часов
13%
6-7 часов
7%
больше 7 часов
9%
больше 8 часов (постоянно перерабатываю)
👍5
День 2180. #Оффтоп
Почему Открытый Код Важен? И Бесплатный ли он? Окончание

Начало
Продолжение

Нужно ли платить мейнтейнерам?
Не существует универсального решения. Это зависит от обстоятельств и индивидуальных целей в проекте. Как оправдать эти затраты? Должны ли вы лично донатить? Должна ли компания делать это?

С другой стороны, есть ли ожидания, связанные с этими деньгами? Представьте, что вы получаете $100 доната, и тот же человек лицо/компания открывает тикет или запрос функции, который вы считаете не очень важным. Заставит ли донат вас это реализовывать? Есть мейнтейнеры, которые не берут деньги именно по этой причине.

Не следует ожидать денег от создания ПО с открытым кодом (но получать их, конечно, приятно). По крайней мере, не за тот код, который вы публикуете. Есть компании, как UNO Platform, которые открыли код всех своих продуктов, но вы можете заплатить за контракт на корпоративное обслуживание. Так что, если вам понадобится помощь или функция, они вам помогут.

Приятно иметь стабильный доход от вашего вклада в открытый код, но для большинства проектов этого не будет. Пожертвования хороши, так как сопровождающие, по крайней мере, не терпят убытков в финансовом плане. Они могут нести расходы на серверы, сертификаты и т.д. Если вы цените проект, не стесняйтесь донатить.

Также важно отметить: со стороны мейнтейнеров нет никаких обязательств или гарантий предоставлять поддержку, обновления или определённую функциональность. Они часто работают над проектами в свободное время, движимые страстью или желанием отплатить сообществу. Использование ПО с открытым кодом подразумевает, что между разработчиком и пользователем нет формального контракта. ПО предоставляется «как есть» без гарантий надёжности или пригодности для какой-либо конкретной цели. Это часть стоимости, которую вы платите, если используете какую-либо зависимость.

Зачем вам поддерживать открытый код?
Главное: нет альтернативы, которая была бы лучше в большинстве случаев. У нас нет другой системы, которая бы приносила столько преимуществ по сравнению с недостатками.

Как поддержать открытый код и что я получу?
Самое простое решение (как и со многими проблемами в нашем мире): деньгами. Но это не единственный способ. Тот, кто тратит своё личное время и ресурсы, чтобы улучшить что-то общедоступное - лучше! Если вы создаёте библиотеку, и кто-то пишет вам либо отчёт об ошибке, либо запрос на функцию, либо подаёт пул-реквест, это часто одна из самых высоких наград, которые вы можете получить. Это значит, что кто-то может идентифицировать себя с тем, что вы создали. Мейнтейнеру открытого кода трудно найти кого-то, кто потратит своё время вместе с ним, поэтому время — самый ценный ресурс, который вы можете дать!

Если вы не знаете, как начать, лучше поддерживать небольшие библиотеки, поскольку когнитивная нагрузка для этого меньше. Есть даже целые сайты, такие как https://goodfirstissue.dev/, где вы можете поискать среди репозиториев, кого поддержать.

Что вы получите?
- Знания: изучение чужого кода и шаблонов мышления может помочь вам укрепить ваши собственные хард- и софт-скилы. И, возможно, это даже поможет вам найти работу.
- Нетворкинг: вы познакомитесь с замечательными людьми по всему миру только благодаря вкладам в открытый код.
- Альтруизм: вы делаете добро миру. Ваш код может прямо или косвенно помочь создавать замечательные вещи!

Но, возможно, самое важное. Если вы вносите свой вклад в любой проект с открытым кодом: будьте вежливы и постарайтесь не грубить. Человеческое сотрудничество — это не игра с нулевой суммой! Это совместный вклад в общий успех!

Источник: https://steven-giesel.com/blogPost/591f2354-a205-4507-bbb2-7d88781e0563/why-is-open-source-important-and-is-it-free
👍13
День 2181. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 39. Даже небольшие физические расстояния препятствуют общению и совместной работе


Совместная работа наиболее эффективна, когда коллеги находятся рядом и могут общаться беспрепятственно. Даже если ваш коллега находится в другом кабинете, до которого нужно дойти, либо использовать телефон, коммуникация уже усложняется. Тем более, если вы разделены географически.

Чем больше часовых поясов разделяют общающихся, тем серьёзнее проблема со связью и тем меньше количество рабочих часов, в течение которых люди могут взаимодействовать. В одном проекте участвовали люди из нескольких стран, находящихся в 12 часовых поясах. Какое бы время они ни выбрали для созвонов, оно было для кого-то неудобным. Время постоянно менялось, чтобы неудобства причинялись всем примерно в равной степени. Эта любезность демонстрировала уважение к каждому и давала важный культурный сигнал, указывающий, что все люди независимо от их места работы одинаково важны для проекта.

Организация офисных помещений так, чтобы внутренняя обстановка способствовала совместной работе и при этом сохранялась приватность, позволяющая сотрудникам быть сосредоточенными на работе, — тонкое искусство. Работники умственного труда наиболее продуктивны, когда входят в состояние потока, глубоко погружаясь в задачу. Поддержание состояния потока требует отсутствия отвлекающих факторов. Т.е. люди должны быть достаточно близко друг к другу, чтобы легко взаимодействовать, но им также необходимо личное пространство для сосредоточения внимания.

Виртуальные команды: максимальное разделение
Все больше IT-специалистов работают удалённо. Однако возникновение виртуальных команд порождает новый набор сложностей с организацией культуры сотрудничества. Трудно узнать и понять коллег, которых вы никогда не встречали лично, не говоря уже о том, чтобы выстроить общую культуру ценностей и практик или интегрироваться в неё.

Гугл по запросу «сложности виртуальных команд разработчиков ПО» выдаёт множество статей, посвященных соответствующим проблемам. Если вы работаете удалённо, стоит потратить время, чтобы изучить основные сложности и подумать, как добиться успеха.

Дверь, дверь, королевство за дверь!
Закрытая дверь — барьер для общения. В зависимости от того, по какую сторону двери вы находитесь, она может быть преимуществом или неудобством. Если двери нет, то это способствует общению, с другой стороны, как сообщить коллегам, что вы заняты? Можно использовать табличку с надписью «Свободен» с одной стороны и «Занят» с другой. Либо какой-то другой похожий сигнал. Но надо договориться об этом с коллегами, потому что далеко не все понимают намёки. В некоторых компаниях эксперты, оказывающие консультативные услуги, устанавливают рабочие часы, в течение которых к ним можно прийти для обсуждения назревших вопросов.

К работе в популярных среди компаний по разработке ПО опенспейсах тоже нужно привыкнуть, и далеко не у всех это получается. Многие испытывают сильный стресс, пока не адаптируются к шумной среде.

Не существует идеального решения для организации рабочих пространств, которые могли бы удовлетворить противоречивые потребности в приватности, свободе от отвлекающих факторов, близости к коллегам в целях быстрого взаимодействия и общем пространстве для совещаний по проекту. Географическое разделение вызывает проблемы, а культурные различия значительно усложняют эффективное сотрудничество. Инструменты для совместной работы помогают сотрудничать людям, разделённым большими расстояниями, но не могут полностью заменить личное взаимодействие в реальном времени. По мере возможности руководители должны позволять членам команды самим организовывать их рабочие места, чтобы наилучшим образом сбалансировать эти противоречивые цели. А если это невозможно, есть наушники или беруши.

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 5.
👍12👎9
День 2182. #МоиИнструменты
SQL-запросы к Логам и Другим Текстовым Данным
Сразу оговорюсь, что этот инструмент древний как мамонты, и в эпоху структурированных логов, Seq и прочих мощных визуализаторов, он, возможно, мало кому будет интересен, но вдруг. Я обнаружил его только недавно по необходимости.

Попросил меня коллега выбрать из логов IIS айпишники клиентов, которые обращались к определённому URL. Логи IIS – это текстовые файлы вот такого формата:
#Fields: date time s-sitename s-computername s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs-version cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken
2025-01-15 00:00:00 W3SVC7 WWW-VM-B 127.0.0.1 GET /page.html - 443 - 8.8.8.8 HTTP/1.1 Mozilla/5.0+… - 200 0 0 103

И дальше ещё много таких строчек (в моём случае было 2 файла по 1Гб+).

Так вот, у Microsoft есть утилита Log Parser которая обеспечивает универсальный доступ через SQL-запросы к текстовым данным, таким как файлы логов, XML-файлы и CSV-файлы, а также к источникам данных в операционной системе Windows, таким как журнал событий, реестр, файловая система и Active Directory.

Но это утилита для командной строки, а для любителей GUI, есть Log Parser Studio (скачать можно здесь), которая работает поверх Log Parser. На картинке выше пример результата запроса к логам IIS. Кроме того, Log Parser Studio позволяет выполнять разные запросы на нескольких вкладках, экспортировать результаты, сгенерировать скрипт для PowerShell, чтобы выполнять его на любой машине, где установлен Log Parser, а также содержит десятки шаблонов популярных запросов.

В общем, небольшая, но полезная в отдельных случаях утилитка.

Источник: https://techcommunity.microsoft.com/blog/exchange/log-parser-studio-2-0-is-now-available/593266
👍30
День 2183. #TipsAndTricks
Упрощаем Отладку Сторонних Типов

При отладке часто бывает полезно иметь возможность проверять состояние объектов в отладчике. Однако, когда вы работаете со сторонними типами, которые не имеют форматирования при отладке, вы не можете легко посмотреть их внутреннее состояние. Сегодня посмотрим, как добавить форматирование при отладке к сторонним типам.

DebuggerDisplayAttribute — очень полезный атрибут, позволяющий указать, как объект должен отображаться в отладчике. Ваши типы можно пометить этим атрибутом так:
[DebuggerDisplay("Name = {Name}, Age = {Age}")]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

var person = new Person { Name = "John", Age = 42 };


Это заставит отладчик отображать экземпляр объекта Person как Name = John, Age = 42.

Но что, если вы работаете со сторонним типом, в который вы не можете добавить атрибут DebuggerDisplay?

Существует менее известное свойство Target, которое определяет, к какому типу следует применять атрибут DebuggerDisplay. Мы можем использовать его в сочетании с атрибутом assembly, чтобы добавить любому типу форматирование при отладке. Для простоты добавим его для типа Task, хотя он уже отображается отформатированным при отладке.
[assembly: DebuggerDisplay("Is Task finished: {IsCompleted}", Target = typeof(Task))]

var task = Task.Delay(100_000);
await task;

Теперь при наведении мыши на переменную task вы увидите результат, похожий на тот, что на картинке ниже.

У этого подхода есть некоторые «естественные» недостатки:
1. Вы можете помечать только публичные типы или интерфейсы по очевидным причинам.
2. Может быть неочевидно, откуда берётся отладочная информация. Особенно, если у вас большая кодовая база, и кто-то в её «потайном уголке» помечает тип атрибутом DebuggerDisplay для форматирования при отладке.

Источник: https://www.meziantou.net/hsts-for-httpclient-in-dotnet.htm
👍35
День 2184. #ЗаметкиНаПолях
Масштабируем Монолиты. Начало
Монолиты среди разработчиков пользуются дурной славой. Считается что они устарели, не масштабируются и что для успеха нам нужны микросервисы. Это неправда. Хорошо спроектированный монолит - часто правильный выбор и может хорошо масштабироваться. Ключ к успеху — понимание ваших потребностей в масштабировании и применение правильных решений в нужное время.

Понимание масштаба
Монолит помещает весь код в одну развёртываемую единицу. Его преимущества - более быстрые циклы разработки, более простые отладка и развёртывания. Но по мере роста системы вы столкнётесь с проблемами масштабирования:
- Запросы к БД замедляются по мере роста объёма данных;
- API, отлично работавшее с сотнями пользователей, начинает тормозить при тысячах;
- Время сборки увеличивается по мере расширения кодовой базы.
Это естественные проблемы роста, с которыми сталкивается каждая успешная система.

1. Вертикальное масштабирование
Это предоставление приложению большего количества ресурсов на одной машине. Самая простая стратегия масштабирования и часто самый эффективный первый шаг. Прежде чем погружаться в сложные распределённые системы, подумайте, может ли модернизация существующей инфраструктуры решить проблемы с производительностью.

Вертикальное масштабирование особенно хорошо работает, когда есть явные узкие места в ресурсах. Если ЦП постоянно загружен более чем на 80%, поможет добавление дополнительных ядер. Если тормозит ввод-вывод в БД, обновление до более быстрого хранилища может значительно повысить производительность. Современные облачные платформы делают это чрезвычайно простым.

Преимущества
Поддерживается простота системы: не нужно перепроектировать архитектуру, внедрять новые шаблоны развёртывания или управлять сложностью распределённой системы. Процедуры мониторинга, отладки и эксплуатации остаются прежними.

Ограничения
В итоге вы достигнете потолка того, что может обработать одна машина. В облаке есть ограничения мощности машин, а затраты обычно экспоненциально увеличиваются. Кроме того, одна машина — это единая точка отказа.

За чем следить:

- Расходы растут быстрее, чем пользовательская база;
- Нужна лучшая избыточность и отказоустойчивость;
- Время простоя развёртывания влияет на бизнес-операции;
- Самый большой доступный размер машины приближается к 70% использования.

2. Горизонтальное масштабирование
Запускает несколько экземпляров приложения за балансировщиком нагрузки. Обеспечивает улучшенную отказоустойчивость и почти линейные возможности масштабирования.

Ключ к успешному горизонтальному масштабированию в дизайне приложения. Оно не должно сохранять состояние — каждый запрос должен содержать всю информацию, необходимую для его обработки. Это означает:
- Аутентификация через токены (например, JWT), а не сессии на сервере;
- Распределённое кэширование.

Балансировщик нагрузки играет решающую роль в этой архитектуре. Его задача — распределять трафик по экземплярам приложения. Популярные варианты включают:
- nginx: мощный, с открытым исходным кодом, отлично подходит для пользовательских конфигураций;
- YARP: обратный прокси от Microsoft, отлично подходит для приложений .NET;
- Облако: AWS ALB, Azure Application Gateway, Google Cloud Load Balancing

Преимущества
- Лучшая отказоустойчивость;
- Возможность обрабатывать больше пользователей одновременно;
- Последовательные развёртывания с нулевым временем простоя;
- Экономически эффективное масштабирование (отключение ненужных экземпляров при низком трафике).

Недостатки
В основном не технические, а архитектурные. Приложение должно быть спроектировано для горизонтального масштабирования с самого начала. Преобразование приложения с сохранением состояния в приложение без него часто требует значительного рефакторинга.

Продолжение следует…

Источник:
https://www.milanjovanovic.tech/blog/scaling-monoliths-a-practical-guide-for-growing-systems
👍16👎1
День 2185. #ЗаметкиНаПолях
Масштабируем Монолиты. Продолжение

Начало

3. Масштабирование БД
Производительность БД - то, где большинство монолитов впервые сталкиваются с реальными ограничениями. Рассмотрим подробно стратегии масштабирования.

Реплики чтения
Часто это первый шаг в масштабировании БД. Создаются копии основной БД, которые обслуживают трафик только для чтения. Каждая реплика поддерживает актуальную копию данных посредством репликаций. Изменения передаются в одном направлении: от основной БД к репликам. Т.е. любые данные, записанные в основную БД, в итоге появятся в репликах. «В итоге», т.е. с задержкой. Так вы жертвуете согласованностью данных ради лучшей производительности чтения.
Большинство поставщиков облачных услуг поддерживают реплики чтения с минимальной настройкой, выполняя репликацию, мониторинг и следя за отказоустойчивостью за вас.

Важно:
- Задержка репликации влияет на актуальность данных;
- Объём записи влияет на скорость репликации;
- Географическое местоположение влияет на задержку;
- Каждая реплика увеличивает ваши расходы.

Материализированные представления
Это предварительно вычисленный набор данных, хранящийся в виде таблицы. В отличие от обычных представлений, которые вычисляют результаты для каждого запроса, материализованные представления сохраняют результаты. Это значительно ускоряет их выполнение, но создает новую проблему: поддержание их актуальности.

Сценарии использования:
- Сложные аналитические запросы;
- Данные, которые обновляются по расписанию;
- Агрегации и сводки;
- Денормализация данных для определённых представлений.

Ключевой компромисс — актуальность против производительности. Нужно решить, как часто обновлять материализованные представления. Слишком часто — нагрузка на БД, слишком редко — данные устаревают.

Шардирование БД
Разделяет данные между несколькими экземплярами БД, причём каждый содержит отдельное подмножество данных.

Шардирование по диапазону разделяет данные на основе диапазонов значений ключа. Например, клиенты А-М – в раздел 1, Н-Я — в раздел 2. Подход хорошо работает с данными, имеющими естественное распределение диапазонов: даты или алфавитный порядок, - но может привести к появлению точек перегрузки, если определённые диапазоны будут запрашиваться чаще, чем другие.

Шардирование по хэшу применяет хэш-функцию к ключу, чтобы определить, в какой раздел поместить данные. Выбор функции хэширования имеет решающее значение. Она должна равномерно распределять данные по разделам, чтобы ни один не стал узким местом. Этот подход обеспечивает лучшее распределение данных, но усложняет запросы на основе диапазонов, поскольку связанные данные могут находиться в разных разделах.

Шардирование по клиенту предоставляет каждому клиенту собственную БД. Подход обеспечивает естественную изоляцию и упрощает операции, специфичные для клиента. Хотя он усложняет запросы между клиентами, он часто является самым чистым решением для мультитенантных систем, где важна изоляция данных.

Окончание следует…

Источник:
https://www.milanjovanovic.tech/blog/scaling-monoliths-a-practical-guide-for-growing-systems
👍16
День 2186. #ЗаметкиНаПолях
Масштабируем Монолиты. Окончание

Начало
Продолжение

4. Кэширование
Один из самых эффективных способов повышения производительности системы. Правильно реализованная стратегия кэширования может значительно снизить нагрузку на БД и улучшить время отклика за счёт хранения часто используемых данных в памяти.

Современное кэширование происходит на нескольких уровнях:
- Кэширование в браузере сокращает ненужные сетевые запросы;
- CDN-кэширование приближает контент к пользователям;
- Кэширование на уровне приложений с помощью таких инструментов, как Redis, сохраняет часто используемые данные в памяти;
- Кэширование запросов к БД сокращает дорогостоящие вычисления.

Ключ к эффективному кэшированию — понимание ваших паттернов доступа к данным. Часто читаемые, редко изменяемые данные получат наибольшую выгоду от кэширования. Такие инструменты, как Redis, Memcached или гибридный кэш, отлично справляются с хранением таких данных в памяти, обеспечивая время доступа менее миллисекунды. Поставщики облачных услуг предлагают управляемые сервисы кэширования, такие как Azure Cache для Redis, которые справляются с эксплуатационной сложностью обслуживания распределённого кэша.

5. Очереди сообщений
Позволяют откладывать трудоёмкие операции и распределять работу между несколькими обработчиками. Это сохраняет отзывчивость приложения путём обработки тяжёлых задач в фоновом режиме.

Очереди сообщений преобразуют поведение системы под нагрузкой. Вместо того чтобы обрабатывать всё синхронно, вы можете ставить работу в очередь на потом. Этот шаблон особенно хорошо работает для таких операций, как:
- обработка загруженных файлов,
- отправка писем и уведомлений,
- создание отчётов,
- обновление поисковых индексов,
- выполнение пакетных операций.

Настоящая сила очередей сообщений заключается в их способности справляться с пиками трафика. Когда система сталкивается с всплеском запросов, очереди действуют как буфер. Они позволяют вам принимать работу с пиковой скоростью, но обрабатывать её в равномерном темпе.

Итого
Масштабирование монолита не заключается в выборе между вертикальным или горизонтальным масштабированием, кэшированием или любым другим подходом. Важно использовать правильный инструмент в правильное время. Начните с самого простого решения, которое решает вашу непосредственную проблему, а затем добавляйте сложность при необходимости.

Практический путь масштабирования:
1. Оптимизируйте код и запросы к БД;
2. Добавьте кэширование там, где это наиболее важно;
3. Масштабируйте вертикально, пока это экономически эффективно;
4. Переходите к горизонтальному масштабированию для лучшей отказоустойчивости;
5. Внедряйте очереди сообщений для фоновой работы;
6. Рассмотрите возможность шардирования БД, когда этого требует размер данных.

Хорошо спроектированный монолит может выдерживать значительную нагрузку, используя лишь часть этих методов. Главное — понять фактические узкие места вашей системы и устранить их конкретно. Не позволяйте лучшему быть врагом хорошего. Начните с самого простого решения, которое может работать, измерьте всё и масштабируйте то, что необходимо.

Источник: https://www.milanjovanovic.tech/blog/scaling-monoliths-a-practical-guide-for-growing-systems
👍11
День 2187. #ЗаметкиНаПолях
Используем Roslyn для Анализа и Изменения Кода Решения
Roslyn можно использовать как библиотеку для анализа и генерации кода. Например, вы можете создать консольное приложение, которое загружает решение, находит шаблоны и переписывает код.

Создадим консольное приложение и добавим необходимые NuGet-пакеты:
dotnet new console
dotnet add package Microsoft.Build.Locator
dotnet add package Microsoft.CodeAnalysis.CSharp
dotnet add package Microsoft.CodeAnalysis.CSharp.Workspaces
dotnet add package Microsoft.CodeAnalysis.Workspaces.Common
dotnet add package Microsoft.CodeAnalysis.Workspaces.MSBuild


Вот пример, как создать рабочее пространство Roslyn из решения:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.MSBuild;

// Находим сборки MSBuild в вашей системе
Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();

// Возможно надо восстановить NuGet-пакеты решения перед тем, как открыть его в Roslyn.
// Зависимости могут потребоваться для корректного анализа.

// Создаём рабочее пространство Roslyn и загружаем решение
var ws = MSBuildWorkspace.Create();
var solution =
await ws.OpenSolutionAsync(@"my_solution.sln");


Теперь можно работать с решением. Вот несколько примеров.

Анализируем синтаксические деревья:
foreach (var proj in solution.Projects)
{
foreach (var doc in proj.Documents)
{
var syntree = await doc.GetSyntaxTreeAsync();
var semanticModel = await doc.GetSemanticModelAsync();
// Анализируем дерево
// Можно использовать CSharpSyntaxWalker для обхода
}
}


Получаем все ссылки на символ:
var comp = 
await solution.Projects.First().GetCompilationAsync();
var symbol =
comp.GetSpecialType(SpecialType.System_String);
var refs =
await SymbolFinder.FindReferencesAsync(symbol, solution);


Переименовываем символ
var symbol = comp
.GetSymbolsWithName(s => s == "MyMethod")
.Single();
var newSolution = await Renamer
.RenameSymbolAsync(
solution,
symbol,
new SymbolRenameOptions()
{
RenameOverloads = true
},
"MyMethod2");
ws.TryApplyChanges(newSolution);


Обновляем документ с помощью DocumentEditor
foreach (var proj in solution.Projects)
{
foreach (var doc in proj.Documents)
{
var editor =
await DocumentEditor.CreateAsync(doc);

// Изменяем
foreach(var emptyStatement in
editor
.OriginalRoot
.DescendantNodes()
.OfType<EmptyStatementSyntax>())
{
editor.RemoveNode(emptyStatement);
}

// Применяем изменения
var newDoc = editor.GetChangedDocument();
if (!ws.TryApplyChanges(newDoc.Project.Solution))
{
Console.WriteLine("Failed!");
}
}
}


Обновляем документ с помощью CSharpSyntaxRewriter:
foreach (var proj in solution.Projects)
{
foreach (var doc in proj.Documents)
{
// Изменяем
var root = await doc.GetSyntaxRootAsync();
if(root is null)
continue;

var newRoot =
new CustomRewriter().Visit(root);
var newDoc =
doc.WithSyntaxRoot(newRoot);

// Применяем изменения
if (!ws.TryApplyChanges(newDoc.Project.Solution))
{
Console.WriteLine("Failed!");
}
}
}

internal sealed class CustomRewriter :
CSharpSyntaxRewriter
{
public override SyntaxNode?
VisitIfStatement(IfStatementSyntax node)
=> node.WithLeadingTrivia(
SyntaxFactory
.ParseLeadingTrivia("/* comment */"));
}


Выберите любое «подопытное» решение, введите его путь вместо my_solution.sln, вставьте любой из примеров и можете пройти его в отладчике пошагово, чтобы посмотреть, как работает анализатор.

Источник: https://www.meziantou.net/using-roslyn-to-analyze-and-rewrite-code-in-a-solution.htm
👍19
День 2188. #УрокиРазработки
Уроки 50 Лет Разработки ПО


Урок 40. Неформальные подходы, используемые небольшими сплоченными командами, плохо масштабируются

Гейтс и Аллен, Джобс и Возняк, Хьюлетт и Паккард. Скорее всего, ни у кого из них не было письменных инструкций для их ранних проектов. Двум гениям, работающим бок о бок, не нужна документация. Но это трудно масштабировать во времени и пространстве, когда этих разумов сотни и при этом они думают на разных языках и погружены в разные культуры. Большие задачи требуют соответствующих подходов.

Процессы и инструменты
По мере роста команд, проектов и организаций возникает потребность в совершенствовании процессов. Люди могут осознать необходимость в этом, только столкнувшись с проблемой. Может случиться, что две команды выполнят одну и ту же работу или какая-то задача будет пропущена, поскольку все думали, что её сделает кто-то другой. Эти проблемы требуют более чёткого планирования, координации и коммуникации между членами команды и группами. По мере вовлечения в работу все большего количества людей, особенно если они находятся в разных местах, появляются все новые источники разногласия и задержек.

Более крупные проекты требуют письменного оформления большего объёма информации, чтобы другие участники знали, что происходит и что ждёт впереди. Сохранение требований, решений, бизнес-правил, оценок и метрик в групповом хранилище начинает приносить всё большую пользу. Чем дальше друг от друга члены команды, тем больше внимания нужно уделять организации процесса и тем больше люди вынуждены полагаться на инструменты удалённой совместной работы.

Командам, расположенным в разных местах, нужны инструменты управления задачами, моделирования, тестирования, непрерывной интеграции и особенно управления кодом. ПО с открытым кодом — яркий пример распределённой разработки. Как отмечает Эндрю Леонард: «Ни одно нововведение не было столь же важным для разработки ПО распределенными командами, как появление систем управления версиями.»

Необходимость специализации
Работая над проектом в одиночку, вы поочередно выполняете все роли участников проекта. В каждый конкретный момент вы можете действовать как бизнес-аналитик, дизайнер, программист, тестировщик, составитель документации, руководитель проекта или специалист по сопровождению.

Нереально ожидать, что каждый член команды будет в полной мере владеть всеми этими ролями. По мере роста проектов и команд специализация некоторых навыков становится естественной и полезной. В какой-то момент даже Билл Гейтс и Пол Аллен решили, что им нужно нанять людей, обладающих навыками, отличными от программирования, чтобы обеспечить рост своей молодой компании: технического писателя, математика, руководителя проекта и т.д. Я слышал, у них это сработало.

Коммуникационные конфликты
По мере увеличения группы количество коммуникационных каналов растёт экспоненциально. Участникам проекта нужны механизмы, позволяющие быстро и точно обмениваться информацией. При этом люди инстинктивно выбирают способы общения, наиболее удобные для них, но не удобные для другой стороны. В проектах с участием нескольких команд важно заранее заложить основу для общения, используя те способы, которые будут эффективно работать для всех. Кроме того, команды могут по-разному принимать решения. Участникам следует обсудить, как будут приниматься совместные решения до того, как все столкнутся с первой серьезной проблемой.

Успешные организации растут и берутся за всё более сложные задачи. Не стоит ожидать, что методы, эффективные для нескольких человек в гараже, подойдут и для больших распределённых команд. Они могут избежать некоторых трудностей, заранее предусмотрев процессы и инструменты, необходимые для интеграции различных команд. Если участники будут иметь возможность общаться и понимать коллег, работающих удалённо, на ранних этапах проекта, это поможет избежать культурных конфликтов. Потратив время на то, чтобы заложить эти основы, можно предотвратить много неприятностей в будущем.

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 5.
👍5👎3
День 2189. #Безопасность
CORS. Использование Ресурсов Между Разными Источниками. Начало
Рассмотрим, что было бы, если бы можно было использовать JavaScript для вызова конечной точки из другого источника.

См. определение источника тут.

Ниже показан простой веб-API https://localhost:3000 с конечными точками Get() и Post():
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Controllers;

[Route("api/[controller]")]
[ApiController]
public class CorsController : ControllerBase
{
// GET: api/Cors
[HttpGet]
public int Get()
{
return new Random().Next(10, 100);
}
// POST api/Cors
[HttpPost]
public void Post([FromBody] int value)
{
}
}

Get() возвращает случайное число между 10 и 99, а Post() ничего не делает.

В другом проекте, в другом источнике https://localhost:5000, для вызова этого API страница Razor использует JavaScript:
@page
<div class="text-center">
<h1 class="display-4">CORS</h1>
<div id="output"></div>
</div>
@section Scripts {
<script>
fetch("https://localhost:3000/api/Cors")
.then(response => response.json())
.then(data => {
document
.getElementById("output")
.textContent = data;
});
</script>
}

Код JavaScript использует метод fetch() для вызова API на другом сервере, а затем выводит результаты. Однако выполнение кода приводит тому, что случайное число не отображается, а на вкладке Сеть в инструментах разработчика браузера вызов API выделен красным цветом.

Запрос был отправлен, но JS не получил никаких данных. Причина: правило ограничения домена (CORS). Если бы этот запрос работал, злоумышленник мог бы использовать JS для отправки HTTP-запроса на страницу, чтобы, например, добавить товар в корзину – автоматически с использованием файла cookie идентификатора сессии пользователя (см. также Безопасность Cookie). В рамках анализа ответа злоумышленник мог бы извлечь antiforgery-токен из формы и, следовательно, обойти защиту от межсайтовой подделки запросов.

И хотя данный вид атаки ещё не был изобретен, когда появилось правило ограничения домена, поскольку JS не может получить доступ к HTTP-ответу (спасибо правилу ограничения домена), атака не сработает.

Продолжение следует…

Источник: Кристиан Венц “Безопасность
ASP.NET Core”. М.: ДМК Пресс, 2023. Глава 4.
👍16
День 2190. #Безопасность
CORS. Использование Ресурсов Между Разными Источниками. Продолжение

Начало

Но что, если у JavaScript кода приложения есть законный интерес к вызову API? Если внимательно посмотреть на HTTP-запрос, который был сгенерирован, когда мы попытались вызвать API из JavaScript, мы заметим кое-что любопытное. Заголовок Origin не отправляется с каждым запросом, но браузер автоматически добавляет его при кросс-доменном запросе:
GET /api/Cors HTTP/2
Host: localhost:3000

Origin: https://localhost:5000

Заголовок содержит источник – протокол, домен, порт – вызывающей страницы. Тогда сервер может взять эту информацию и решить, может ли код JavaScript иметь доступ к данным, поступающим с сервера. Если да, то HTTP-ответ должен включать заголовок Access-Control-Allow-Origin и в качестве значения для него должен использовать отправленный источник (или заполнитель *, но, как всегда, лучше быть как можно более явным):
Access-Control-Allow-Origin: https://localhost:5000

Этот механизм называется совместное использование ресурсов между источниками (CORS), и ASP.NET Core поддерживает его – нет необходимости реализовывать его вручную. В диспетчере пакетов NuGet по запросу «CORS» выдаётся длинный список результатов. Но все они предназначены для .NET Standard и .NET Framework. ASP.NET Core автоматически использует собственную версию пакета Microsoft.AspNetCore.Cors. CORS поставляется как промежуточное ПО в ASP.NET Core, поэтому нужно сделать его доступным и активировать. В классе Program требуется:
- добавить поддержку CORS (и настроить её), с помощью AddCors();
- активировать CORS - UseCors().

При вызове AddCors() можно указать параметры. Обычно нужно настроить хотя бы одну политику CORS (т.е. определить разрешённые источники):
builder.Services.AddCors(opts =>
{
opts.AddPolicy("CORS API Endpoint",
bldr =>
{
bldr.WithOrigins("https://localhost:5000");
});
});

Важно:
- Используйте источник клиента API, а не URL самого API!
- Источник должен состоять только из протокола, домена и порта, без слеша в конце.

Можно использовать произвольное количество политик, и у каждой может быть любое количество источников. Переменная bldr (типа CorsPolicyBuilder) также предоставляет методы, чтобы явно разрешить другие фрагменты информации для кросс-доменного запроса с использованием JS:
- WithExposedHeaders() – список заголовков HTTP-ответа, которые возвращаются и становятся доступными для JS;
- WithHeaders() – список разрешённых дополнительных заголовков HTTP-запросов, к которым клиент хотел бы получить доступ;
- WithMethods() – список разрешённых дополнительных HTTP-методов.
- AllowAnyHeader() – разрешать все заголовки;
- AllowAnyMethod() – разрешать все методы.

По умолчанию никакие учётные данные, например заголовки Authorization, не отправляются вместе с кросс-доменным запросом. Опять же, причина
тому – защита от межсайтовой подделки запросов. Если вы хотите, чтобы эта информация была доступна для API, метод AllowCredentials() сообщает клиенту, что её можно отправлять, а DisallowCredentials() - явно запрещает.

Наконец, можно использовать политику по умолчанию:
options.AddDefaultPolicy(
bldr =>
{
bldr.WithOrigins("https://localhost:5000");
});


Окончание следует…

Источник: Кристиан Венц “Безопасность
ASP.NET Core”. М.: ДМК Пресс, 2023. Глава 4.
👍16
День 2191. #Безопасность
CORS. Использование Ресурсов Между Разными Источниками. Окончание

Начало
Продолжение

Теперь, когда политика настроена, пришло время её использовать. В зависимости от приложения, возможно, вы захотите, чтобы она применялась глобально, только для определённых конечных точек или маршрутов.

1. Глобально
Нужна политика по умолчанию, затем нужно добавить вызов метода UseCors(). Важен порядок. CORS необходимо активировать:
- после маршрутизации (UseRouting()),
- перед кешированием ответов, аутентификацией и авторизацией (UseResponseCaching(), UseAuthentication(), UseAuthorization()).

Для политики по умолчанию достаточно вызвать:
app.UseCors();


2. Для конечных точек
Для именованных политик также нужен вызов UseCors() в Program.cs, но т.к. нет политики по умолчанию, затем необходимо добавить атрибут [EnableCors] с именем политики в класс контроллера (для всех методов) или только в отдельные методы:
[Route("api/[controller]")]
[EnableCors("CORS API Endpoint")]
[ApiController]
public class CorsController : ControllerBase
{

}

Теперь все методы в контроллере API отправляют правильный заголовок Access-Control-Allow-Origin. При этом атрибут [DisableCors] отключит политику для отдельных методов контроллера. Применив эти изменения к API и снова вызвав страницу из изначального примера, мы получаем желаемый результат. На этот раз не только проходит HTTP-запрос, но и код JS может получить доступ к возвращаемому значению и отобразить его.

3. Для маршрутов
Применяе именованные политики к маршрутам. В ASP.NET Core это настраивается в методе app.UseEndpoints() с помощью расширения RequireCors() после вызова MapControllers(), MapControllerRoute(), MapGet() или аналогичных:
app.UseEndpoints(eps =>
{
eps.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.RequireCors("CORS API Endpoint");
});


CORS на стероидах: предварительные запросы
GET-запрос из нашего примера всё равно выполняется, даже если не соответствует политике (т.к. считается, что GET-запросы не изменяют состояния приложения), но код JS не получает доступа к возвращаемым данным. POST (и другие) запросы могут изменить состояние приложения, поэтому их нельзя просто отправить. В таких случаях браузеры отправляют предварительные запросы (preflight request). Сначала - запрос OPTIONS. Он также не должен ничего изменить на сервере. Если ответ содержит заголовок Access-Control-Allow-Origin с соответствующим значением, и ответ положительный, на сервер будет отправлен ещё один запрос, чтобы получить ресурсы.

Поскольку REST API не имеют состояния, сервер должен проверять все запросы. Но мы можем указать (через заголовок Access-Control-Max-Age), что предварительный запрос не обязательно делать каждый раз. В .NET это можно легко настроить внутри метода AddCors:
builder.Services.AddCors(opts =>
{
opts.AddPolicy("CORS API Endpoint",
bldr =>
{
bldr.

.SetPreflightMaxAge(TimeSpan.FromMinutes(10));
});
});

SetPreflightMaxAge даст указание клиенту кэшировать предварительный запрос CORS на 10 минут (т.е. он отправляет заголовок Access-Control-Max-Age со значением 600).

Источники:
- Кристиан Венц “Безопасность
ASP.NET Core”. М.: ДМК Пресс, 2023. Глава 4.
-
https://steven-giesel.com/blogPost/5fc7cb62-d5ad-4f07-831c-d6c1c6974641/cache-cors-preflight-requests
👍13
День 2192. #Оффтоп
Когда Прекратится Поддержка .NET Framework?

Вы когда-нибудь задумывались, когда мы наконец избавимся от старого доброго .NET Framework? Короткий ответ: непонятно. Майкрософт обещают продолжать его поддерживать, но это ведь не может продолжаться бесконечно.

Если посмотреть официальную страницу, вы увидите табличку на картинке выше.

Итак, все версии выше .NET Framework 4.7 по-прежнему поддерживается (и даже у 4.6.2 всё ещё 2 года в запасе). Примечание: эти версии практически не имеют конфликтов, поэтому обновление с 4.6.2 до 4.8.1 не должно вызвать никаких проблем.

В любом случае, одним из основных факторов долговечности фреймворка является то, что он связан с Windows. Windows Server 2022 поставляется с .NET 4.8.1. Дата окончания поддержки Windows Server 2022 — октябрь 2031 года. И даже Windows Server 2025 (последняя версия) содержит фреймворк, а её расширенная поддержка заканчивается ещё через 3 года — в 2034 году.

Поэтому весьма вероятно, что .NET Framework 4.8.x будет существовать плюс-минус до этого момента. Возможно, будут выпущены более новые версии с исправлениями из-за проблем с безопасностью (например, SHA-2 устареет или в нём обнаружится уязвимость, и людям придётся перейти на что-то другое).

Конечно, для новой разработки следует использовать .NET 8 и более поздние версии, если возможно. Однако очень маловероятно, что .NET Framework перестанет работать в ближайшее время. Хорошо это или нет, решать вам.

Источник: https://steven-giesel.com/blogPost/6de02565-a08e-4968-b610-22826582c1f1/when-will-net-framework-retire
👍15
День 2193.
Каналу 6 лет!

Минул ещё один год. Вас набралось почти 6000. Спасибо, что читаете, лайкаете и комментируете.

Для тех, кто пришёл недавно, вот здесь подборка тематических тегов. Кроме того, вы просто можете поискать по сообщениям в канале то, что вас интересует. Это, конечно, не гугл, но всё равно за 6 лет набралось довольно много полезной информации.

Кроме того, добро пожаловать в наш чат, где собралась неплохая компания, и где вам всегда постараются помочь.

Ещё раз спасибо, что читаете, а те, кто кроме этого хочет поддержать, добро пожаловать в Boosty или Patreon. А ещё монетки в телеге появились, если кому так удобнее)))
3👍70👎1
День 2194. #UX
UX Кодов Доступа

Сегодня поговорим на немного отвлечённую тему. Все мы много раз вводили одноразовый код для входа в систему или подтверждения какой-то операции. Дизайн форм для ввода кода везде разный, каждый изощряется, как может. Если ваш продукт отправляет СМС/e-mail с кодами, то пользовательский опыт должен быть максимально хорошим.

Плохо
СМС/e-mail просто не приходит. Пользователь оказывается в подвешенном состоянии и в итоге вынужден искать ссылку «Не получили код? Отправить повторно», а то и ждать по 30-60 секунд перед повторной отправкой. Потом, конечно, после запроса повторной отправки приходит первый код, который уже не работает.

Сообщения вокруг кода длинные или странные. Иногда код приходит в длинном сообщении, где говорится о спаме/фишинге/безопасности, что в итоге делает весь процесс подозрительным. Куча вариантов испортить жизнь пользователю: неясная тема письма, код, зарытый в текст, код, стилистически не отличающийся от остальной части сообщения, код в конце длинного текста, так, что он не показывается в сокращённом уведомлении телефона и приходится его открывать полностью и т.п.

Странная форма. Мне не нравятся формы, где каждая цифра — отдельное поле. Всё должно быть понятно и просто, но тут это не так. Можно ли вставить скопированный код? Куда его вставлять? Перенесутся ли при вставке цифры в отдельные ячейки или вставится только первая? Часто в коде есть дефис он вставится? И т.п.

Код странный. Цифры – хорошо. Буквенно-цифровой код - пойдёт. 12 знаков, включая специальные символы, – зачем?

Запрет копирования/вставки кода. Зачем так делают? Вынуждать пользователя переключаться туда-сюда между приложениями, пытаясь удержать в голове случайные цифры – это издевательство!

Лучше
Код приходит быстро. Не нужно сидеть у почтового ящика, постоянно его обновляя.

Сообщение ясное и краткое. Вероятность того, что я не буду читать этот текст, составляет 99%, поэтому не пишите длинные тексты (ещё лучше – уберите текст вообще!)

Код выделен. Мой разум настроен на «где этот чёртов код?», поэтому выделение кода — это здорово. В Android есть удобная кнопка «Скопировать 434345», которая значительно упрощает процесс. В e-mail используйте форматирование и другие базовые методы дизайна, чтобы выделить код.

Лёгкие копирование и вставка. Конечно, вы можете выделить любой текст, но это может быть болезненно, если код находится в тексте абзаца. Лучше, когда он на отдельной строке и его можно легко скопировать. Идеально, когда приложение автоматически заполняет код и отправляет форму, как только вы его скопировали и переключились обратно на форму.

Отлично
Лучше всего, если этого кошмарного процесса вообще не существует. Я знаю, знаю, безопасность и вот это всё, но это лишние сложности и боль, как смерть от 1000 порезов бумагой. Это похоже на наказание.

Существуют и другие методы двухфакторной аутентификации, но каждый метод приносит свою боль. Приложения для аутентификации только добавляют стресса. А от того, что методы аутентификации в разных приложениях разные: смс/пуш/e-mail/приложение аутентификации, - вообще голова кругом идёт. Я ловлю себя на том, что нажимаю случайные кнопки, в метаниях между Chrome, e-mail клиентом и телефоном, не совсем понимая, что происходит, но в конце концов я нажимаю достаточно, чтобы меня впустили. И не дай бог вернуться спустя годы к какому-то приложению, где был зарегистрирован давно заброшенный e-mail!

В любом случае, между отличным UX и отличной безопасностью есть реальное противоречие, и понятно, что существует множество трудностей и компромиссов, необходимых для достижения баланса. Но, кажется, с этим надо что-то делать. Как минимум, если уж заставлять пользователей проходить через это, то упростить процесс максимально.

Источник: https://bradfrost.com/blog/post/the-ux-of-login-codes/
👍12