День 2696. #ЧтоНовенького
Просмотр Пул-Реквестов в Visual Studio
Интеграция пул-реквестов в VS — одна из самых востребованных функций Git. Разработчики просили о возможности открывать PR, просматривать изменения, обсуждать отзывы и завершать проверку, не переключаясь в браузер. Создавать PR в VS можно с 2024 года. Теперь также можно просматривать, комментировать и утверждать PR из GitHub и Azure DevOps, не выходя из IDE. Обновление доступно в VS 18.7.
Поиск и открытие PR
Вы можете просмотреть список PR для открытого репозитория в окнах Git Repository, Git Changes или из меню Git (см. картинку 1). Если в текущей ветке уже есть активный PR, вы также можете открыть его непосредственно из окна Git Changes.
При открытии PR вы можете увидеть обзор, изменения, коммиты и рецензентов в одном месте. Здесь вы можете выбрать, насколько глубоко хотите изучить код. Вы можете проверить PR, не переключаясь на ветку, что позволяет проверить изменения, сохраняя при этом вашу текущую ветку, незафиксированные изменения и рабочее состояние. Если хотите изучить код более подробно, переключитесь на ветку PR и используйте инструменты навигации, сборки и отладки VS для более глубокого анализа.
Просмотр изменений
Режим просмотра PR разработан для быстрого просмотра. Откройте любой изменённый файл, чтобы увидеть изменения непосредственно в коде или рядом с ним, или используйте режим сводки по нескольким файлам, чтобы увидеть все изменения сразу (см. картинку 2).
Вы также можете просматривать коммиты по отдельности, что полезно, когда PR охватывает несколько логических шагов, и вы хотите понять, как развивалось изменение.
Комментирование и обсуждение
Вы можете оставлять комментарии к определённым строкам, отвечать на ветки обсуждений и завершать обсуждения после их окончания. Файлы с активными комментариями отмечаются в списке изменений, поэтому легко определить, где происходят обсуждения. Все синхронизируется между VS и браузером (см. картинку 3).
При проверке PR в отредактированном коде вы можете одним щелчком применить изменение к вашей рабочей копии. Также Copilot может сгенерировать исправление на основе комментария и окружающего кода, чтобы вы могли сразу же оценить и протестировать его.
Утверждение, завершение и слияние
Когда вы готовы принять/отклонить PR, вы можете увидеть необходимую информацию и действовать, не покидая страницу проверки. На вкладке Overview (Обзор) вы можете увидеть проверки статуса, конфликты слияния и информацию о том, нужны ли ещё необходимые подтверждения. Вы можете утвердить PR из представления сравнения изменений, с дополнительными параметрами голосования для PR Azure DevOps.
Вы также можете завершить или слить PR прямо в IDE. Если планы изменятся, вы можете преобразовать его в черновик или закрыть. После открытия PR вы можете пройти весь процесс проверки в одном месте.
Источник: https://devblogs.microsoft.com/visualstudio/review-pull-requests-without-leaving-visual-studio/
Просмотр Пул-Реквестов в Visual Studio
Интеграция пул-реквестов в VS — одна из самых востребованных функций Git. Разработчики просили о возможности открывать PR, просматривать изменения, обсуждать отзывы и завершать проверку, не переключаясь в браузер. Создавать PR в VS можно с 2024 года. Теперь также можно просматривать, комментировать и утверждать PR из GitHub и Azure DevOps, не выходя из IDE. Обновление доступно в VS 18.7.
Поиск и открытие PR
Вы можете просмотреть список PR для открытого репозитория в окнах Git Repository, Git Changes или из меню Git (см. картинку 1). Если в текущей ветке уже есть активный PR, вы также можете открыть его непосредственно из окна Git Changes.
При открытии PR вы можете увидеть обзор, изменения, коммиты и рецензентов в одном месте. Здесь вы можете выбрать, насколько глубоко хотите изучить код. Вы можете проверить PR, не переключаясь на ветку, что позволяет проверить изменения, сохраняя при этом вашу текущую ветку, незафиксированные изменения и рабочее состояние. Если хотите изучить код более подробно, переключитесь на ветку PR и используйте инструменты навигации, сборки и отладки VS для более глубокого анализа.
Просмотр изменений
Режим просмотра PR разработан для быстрого просмотра. Откройте любой изменённый файл, чтобы увидеть изменения непосредственно в коде или рядом с ним, или используйте режим сводки по нескольким файлам, чтобы увидеть все изменения сразу (см. картинку 2).
Вы также можете просматривать коммиты по отдельности, что полезно, когда PR охватывает несколько логических шагов, и вы хотите понять, как развивалось изменение.
Комментирование и обсуждение
Вы можете оставлять комментарии к определённым строкам, отвечать на ветки обсуждений и завершать обсуждения после их окончания. Файлы с активными комментариями отмечаются в списке изменений, поэтому легко определить, где происходят обсуждения. Все синхронизируется между VS и браузером (см. картинку 3).
При проверке PR в отредактированном коде вы можете одним щелчком применить изменение к вашей рабочей копии. Также Copilot может сгенерировать исправление на основе комментария и окружающего кода, чтобы вы могли сразу же оценить и протестировать его.
Утверждение, завершение и слияние
Когда вы готовы принять/отклонить PR, вы можете увидеть необходимую информацию и действовать, не покидая страницу проверки. На вкладке Overview (Обзор) вы можете увидеть проверки статуса, конфликты слияния и информацию о том, нужны ли ещё необходимые подтверждения. Вы можете утвердить PR из представления сравнения изменений, с дополнительными параметрами голосования для PR Azure DevOps.
Вы также можете завершить или слить PR прямо в IDE. Если планы изменятся, вы можете преобразовать его в черновик или закрыть. После открытия PR вы можете пройти весь процесс проверки в одном месте.
Источник: https://devblogs.microsoft.com/visualstudio/review-pull-requests-without-leaving-visual-studio/
👍6👎1
День 2698. #ЗаметкиНаПолях
Перенос Строки — Это не Только
Когда мы говорим о символах переноса строки, большинство разработчиков думают о
Юникод и несколько механизмов регулярных выражений распознают дополнительные символы переноса строки. Если ваше приложение обрабатывает пользовательский ввод, логи, CSV-файлы или кроссплатформенные данные, это может иметь большее значение, чем вы думаете.
Переносы строк, которые следует знать
Наиболее распространённые символы завершения строки:
Unicode Technical Report #18 (RL1.6) также отмечает эти ограничители:
Вы также можете считать эти пробельные символы переносами строки в некоторых контекстах:
Поэтому полноценное «разбиение по строкам» не должно подразумевать только CR и LF.
Почему это важно в реальном коде
Эти символы могут поступать из:
- Скопированного текста из офисных программ или устаревших систем;
- Файлов, преобразованных с помощью нескольких этапов кодирования и нормализации.
Если вы разделяете текст только по символам
Более безопасный синтаксический анализ в .NET
Если вам нужно разделить текст на строки, отдавайте предпочтение шаблону, который обрабатывает последовательности символов новой строки Unicode.
В .NET шаблон
Если вы хотите избежать аллокации массива строк и строковой переменной на каждую строку, в .NET 9 представлен Regex.EnumerateSplits:
Для нормализации .NET также предоставляет метод
Источник: https://www.meziantou.net/new-lines-are-more-than-r-and-n.htm
Перенос Строки — Это не Только
\r и \nКогда мы говорим о символах переноса строки, большинство разработчиков думают о
\r\n (Windows) и \n (Unix). В большинстве случаев это работает, но это не полная картина.Юникод и несколько механизмов регулярных выражений распознают дополнительные символы переноса строки. Если ваше приложение обрабатывает пользовательский ввод, логи, CSV-файлы или кроссплатформенные данные, это может иметь большее значение, чем вы думаете.
Переносы строк, которые следует знать
Наиболее распространённые символы завершения строки:
\r\n (CRLF, U+000D U+000A)
\n (LF, U+000A)
\r (CR, U+000D)
Unicode Technical Report #18 (RL1.6) также отмечает эти ограничители:
\u0085 (NEL, Next Line)
\u2028 (LS, Line Separator)
\u2029 (PS, Paragraph Separator)
Вы также можете считать эти пробельные символы переносами строки в некоторых контекстах:
\v (VT, Vertical Tab, U+000B)
\f (FF, Form Feed, U+000C)
Поэтому полноценное «разбиение по строкам» не должно подразумевать только CR и LF.
Почему это важно в реальном коде
Эти символы могут поступать из:
- Скопированного текста из офисных программ или устаревших систем;
- Файлов, преобразованных с помощью нескольких этапов кодирования и нормализации.
Если вы разделяете текст только по символам
\r\n или \n, некоторые записи могут остаться объединёнными в одну строку, что может нарушить синтаксический анализ, проверку или формирование отчётов.Более безопасный синтаксический анализ в .NET
Если вам нужно разделить текст на строки, отдавайте предпочтение шаблону, который обрабатывает последовательности символов новой строки Unicode.
using System.Text.RegularExpressions;
string input = "A\u2028B\u0085C\r\nD";
string[] lines = Regex.Split(input, @"(?>\r\n|[\n\v\f\r\u0085\u2028\u2029])");
// lines = ["A", "B", "C", "D"]
В .NET шаблон
\R пока не поддерживается, поэтому приходится использовать более явный шаблон.Если вы хотите избежать аллокации массива строк и строковой переменной на каждую строку, в .NET 9 представлен Regex.EnumerateSplits:
using System;
using System.Text.RegularExpressions;
ReadOnlySpan<char> input = "A\u2028B\u0085C\r\nD";
foreach (Range split in Regex.EnumerateSplits(input, @"(?>\r\n|[\n\v\f\r\u0085\u2028\u2029])"))
{
ReadOnlySpan<char> line = input[split];
ProcessLine(line);
}
static void ProcessLine(ReadOnlySpan<char> line)
{
// обработка line без аллокаций
}
Для нормализации .NET также предоставляет метод
string.ReplaceLineEndings, который полезен, когда необходимо преобразовать все символы конца строки в единое соглашение перед обработкой:string input = "A\u2028B\u0085C\r\nD";
string normalized = input.ReplaceLineEndings("\n");
string[] lines = normalized.Split('\n');
// normalized = "A\nB\nC\nD"
// lines = ["A", "B", "C", "D"]
Источник: https://www.meziantou.net/new-lines-are-more-than-r-and-n.htm
👍6👎1
День 2699. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
36. Управление состоянием
«Расскажите о различных стратегиях управления состоянием, доступных в приложениях .NET. Приведите примеры сценариев, в которых вы бы использовали каждую стратегию».
Хороший ответ
В приложениях .NET эффективное управление состоянием имеет решающее значение для поддержания удобства использования и производительности приложения. Существует несколько стратегий, каждая из которых подходит для разных сценариев.
1. Куки: для хранения пользовательских предпочтений или данных сессии на стороне клиента. Куки легко реализовать в .NET с помощью класса HttpContext:
2. Скрытые поля: полезны для сохранения состояния на стороне клиента при POST-запросах. Они могут содержать данные, которые не должны быть видимыми или изменяемыми пользователями, но должны сохраняться между запросами.
3. Сессии: данные пользователя хранятся на сервере между HTTP-запросами. В .NET 8 состояние сессии можно включить и получить к нему доступ через HttpContext:
4. База данных: используется, когда данные должны сохраняться между различными сессиями или когда объём данных слишком велик для хранения в куки или сессии. Это включает в себя хранение информации о состоянии в БД, к которой можно обращаться по мере необходимости.
5. Кэш: кэширование данных может значительно повысить производительность приложения за счёт снижения нагрузки на БД. В .NET кэширование данных может быть реализовано с использованием кэша в оперативной памяти, распределённого или гибридного кэша:
Выбор правильной стратегии зависит от конкретных требований приложения, включая такие факторы, как масштабируемость, безопасность и характер хранимых данных.
Часто встречающийся неверный ответ:
«Проще всего использовать сессию для хранения всех данных. Всё хранится на сервере, и не нужно беспокоиться об управлении состоянием на стороне клиента.»
Почему это неправильно:
- Чрезмерное использование сессии: ответ предлагает использовать состояние сессии без разбора для всех потребностей управления состоянием, что нецелесообразно из-за проблем с масштабируемостью и производительностью, особенно в распределённых средах, где данные сессии могут не сохраняться на разных серверах без дополнительной настройки.
- Отсутствие масштабируемости: полагаясь исключительно на состояние сессии, можно значительно снизить масштабируемость приложения по мере увеличения числа пользователей, что приводит к увеличению использования памяти и потенциальной потере данных в сценариях веб-ферм, если сессии не настроены на использование сервера состояния или БД.
- Игнорирование других вариантов: не учитываются преимущества других стратегий, которые могут быть более подходящими в зависимости от конкретных требований приложения, таких как необходимость сохранения данных или совместного доступа между сессиями.
Этот часто встречающийся неверный ответ может быть следствием недостаточного понимания последствий различных стратегий управления состоянием или опыта работы в средах, где управление сессиями не являлось узким местом.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
36. Управление состоянием
«Расскажите о различных стратегиях управления состоянием, доступных в приложениях .NET. Приведите примеры сценариев, в которых вы бы использовали каждую стратегию».
Хороший ответ
В приложениях .NET эффективное управление состоянием имеет решающее значение для поддержания удобства использования и производительности приложения. Существует несколько стратегий, каждая из которых подходит для разных сценариев.
1. Куки: для хранения пользовательских предпочтений или данных сессии на стороне клиента. Куки легко реализовать в .NET с помощью класса HttpContext:
public void SetCookie(string key, string value, int? expireTime)
{
CookieOptions option = new();
option.Expires = expireTime.HasValue ?
DateTime.Now.AddMinutes(expireTime.Value) :
DateTime.Now.AddMilliseconds(10);
HttpContext.Response.Cookies.Append(key, value, option);
}
2. Скрытые поля: полезны для сохранения состояния на стороне клиента при POST-запросах. Они могут содержать данные, которые не должны быть видимыми или изменяемыми пользователями, но должны сохраняться между запросами.
3. Сессии: данные пользователя хранятся на сервере между HTTP-запросами. В .NET 8 состояние сессии можно включить и получить к нему доступ через HttpContext:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSession(opts =>
{
opts.IdleTimeout = TimeSpan.FromMinutes(10);
opts.Cookie.HttpOnly = true;
opts.Cookie.IsEssential = true;
});
app.UseSession();
// Сохраняем данные в сессию
HttpContext.Session.SetString("SessionKey", "Value");
4. База данных: используется, когда данные должны сохраняться между различными сессиями или когда объём данных слишком велик для хранения в куки или сессии. Это включает в себя хранение информации о состоянии в БД, к которой можно обращаться по мере необходимости.
5. Кэш: кэширование данных может значительно повысить производительность приложения за счёт снижения нагрузки на БД. В .NET кэширование данных может быть реализовано с использованием кэша в оперативной памяти, распределённого или гибридного кэша:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHybridCache();
// Сохраняем данные в кэш
cache.Set("CacheKey", "Cached Value");
Выбор правильной стратегии зависит от конкретных требований приложения, включая такие факторы, как масштабируемость, безопасность и характер хранимых данных.
Часто встречающийся неверный ответ:
«Проще всего использовать сессию для хранения всех данных. Всё хранится на сервере, и не нужно беспокоиться об управлении состоянием на стороне клиента.»
Почему это неправильно:
- Чрезмерное использование сессии: ответ предлагает использовать состояние сессии без разбора для всех потребностей управления состоянием, что нецелесообразно из-за проблем с масштабируемостью и производительностью, особенно в распределённых средах, где данные сессии могут не сохраняться на разных серверах без дополнительной настройки.
- Отсутствие масштабируемости: полагаясь исключительно на состояние сессии, можно значительно снизить масштабируемость приложения по мере увеличения числа пользователей, что приводит к увеличению использования памяти и потенциальной потере данных в сценариях веб-ферм, если сессии не настроены на использование сервера состояния или БД.
- Игнорирование других вариантов: не учитываются преимущества других стратегий, которые могут быть более подходящими в зависимости от конкретных требований приложения, таких как необходимость сохранения данных или совместного доступа между сессиями.
Этот часто встречающийся неверный ответ может быть следствием недостаточного понимания последствий различных стратегий управления состоянием или опыта работы в средах, где управление сессиями не являлось узким местом.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👎6👍2
День 2700. #ЗаметкиНаПолях
Версионирование API Должно Быть Последним Средством. Начало
Про версионирование API уже была серия постов на канале. Но более важный вопрос не как это делать, а когда. Каждая команда разработчиков API в итоге приходит к одному выводу: «Просто создадим версию 2». Звучит ответственно. За исключением того, что теперь нужно поддерживать два API, два набора документации, два варианта поведения и проект миграции, которую клиенты будут откладывать как можно дольше.
Версионирование — это инструмент совместимости, а не стратегия проектирования.
Большинство изменений API не требуют новой версии. Они требуют более эффективного управления изменениями. Если вы рассматриваете каждое изменение контракта как проблему версионирования, вы в итоге плодите клоны своего API. Если же вы рассматриваете это как проблему управления изменениями, вы начинаете задавать более правильные вопросы:
- Можно ли добавить, а не заменить?
- Может ли старое и новое поведение сосуществовать некоторое время?
- Можно ли ввести новую операцию вместо изменения старой?
- Можно ли безопасно удалить что-то с помощью миграции и основываясь на данных телеметрии?
Такой подход приводит к созданию API, которые гораздо лучше выдерживают проверку временем.
Что на самом деле ломает код клиентов?
Изменения, приводящие к сбоям, обычно касаются не только URL-адреса. Это также:
- удаление или переименование поля,
- изменение значения существующих данных,
- ужесточение проверки запросов,
- изменение формата пагинации или ошибок,
- предположение, что перечисления – закрытый для изменений тип.
Это ломает клиента так же, как и удаление конечной точки:
Вы не изменили путь, не переименовали конечную точку, но всё равно сломали работу клиентов.
Поэтому вместо вопроса: «Должна ли это быть версия 2?», спросите: «Могут ли старый и новый контракты безопасно сосуществовать?»
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/api-versioning-should-be-your-last-resort
Версионирование API Должно Быть Последним Средством. Начало
Про версионирование API уже была серия постов на канале. Но более важный вопрос не как это делать, а когда. Каждая команда разработчиков API в итоге приходит к одному выводу: «Просто создадим версию 2». Звучит ответственно. За исключением того, что теперь нужно поддерживать два API, два набора документации, два варианта поведения и проект миграции, которую клиенты будут откладывать как можно дольше.
Версионирование — это инструмент совместимости, а не стратегия проектирования.
Большинство изменений API не требуют новой версии. Они требуют более эффективного управления изменениями. Если вы рассматриваете каждое изменение контракта как проблему версионирования, вы в итоге плодите клоны своего API. Если же вы рассматриваете это как проблему управления изменениями, вы начинаете задавать более правильные вопросы:
- Можно ли добавить, а не заменить?
- Может ли старое и новое поведение сосуществовать некоторое время?
- Можно ли ввести новую операцию вместо изменения старой?
- Можно ли безопасно удалить что-то с помощью миграции и основываясь на данных телеметрии?
Такой подход приводит к созданию API, которые гораздо лучше выдерживают проверку временем.
Что на самом деле ломает код клиентов?
Изменения, приводящие к сбоям, обычно касаются не только URL-адреса. Это также:
- удаление или переименование поля,
- изменение значения существующих данных,
- ужесточение проверки запросов,
- изменение формата пагинации или ошибок,
- предположение, что перечисления – закрытый для изменений тип.
Это ломает клиента так же, как и удаление конечной точки:
// До
{ "total": 100 }
// После
{ "total": { "amount": 100, "currency": "USD" } }
Вы не изменили путь, не переименовали конечную точку, но всё равно сломали работу клиентов.
Поэтому вместо вопроса: «Должна ли это быть версия 2?», спросите: «Могут ли старый и новый контракты безопасно сосуществовать?»
Продолжение следует…
Источник: https://www.milanjovanovic.tech/blog/api-versioning-should-be-your-last-resort
👍2
День 2701. #ЗаметкиНаПолях
Версионирование API Должно Быть Последним Средством. Продолжение
Начало
Правила совместимости
- Сохраняйте существующие поля и поведение;
- Не превращайте необязательные данные запроса в обязательные;
- Не меняйте то, что делает существующая операция;
- Делайте всё новое в добавление и необязательным по умолчанию.
1. Добавляйте, а не заменяйте
Самое безопасное изменение обычно является аддитивным. Допустим, первоначальный ответ от
Вместо замены поля
Существующие клиенты продолжают использовать
2. Делайте клиентов толерантными
Хорошо работающий клиент не должен выдавать ошибку из-за того, что сервер добавил поле, которое он не понимает. Если ответ изменился с:
на:
существующие клиенты должны игнорировать дополнительное свойство и продолжать работу.
В System.Text.Json неизвестные свойства игнорируются по умолчанию. Реальный риск обычно заключается в чрезмерно строгой проверке JSON (об этом позже на канале). Это одна из распространённых проблем. Команды заявляют о желании обратной совместимости, а затем генерируют клиентские модели, которые отклоняют любое неожиданное поле в ответе. Клиенты должны быть достаточно толерантными, чтобы игнорировать то, чего они не понимают.
3. Не меняйте то, что делает существующая операция
Самые опасные критические изменения скрываются в поведении. URL, тело запроса, формат ответа те же. Но то, что делает операция на сервере, отличается. Например,
Аналогично:
-
-
-
- Веб-хук раньше срабатывал один раз для каждого заказа, а теперь срабатывает для каждой позиции заказа;
и т.п.
Каждый из этих вариантов сохраняет стабильность URL, метода и структуры JSON, но нарушает все существующие интеграции таким образом, что это не будет видно при сравнении схем. Безопасный шаг тот же: добавлять, а не изменять. Например, параметр в конечной точке для жёсткого удаления
Как только операция выпущена, её поведение становится частью контракта. Вы можете добавлять новые операции, можете объявить её устаревшей, но не можете незаметно изменять её работу.
4. Будьте осторожны с валидацией
Существует два варианта одной и той же ошибки:
- Сделать обязательным существующее необязательное поле;
- Добавить новое обязательное поле.
Оба варианта ломают существующих клиентов. Путь к конечной точке не меняется, но запросы, которые раньше выполнялись успешно, теперь отклоняются. Более безопасный путь — определить значения по умолчанию или ввести новую операцию для более строгого рабочего процесса.
Изменения в ответах обычно тщательно проверяются с точки зрения проектирования. Изменения в проверке запросов заслуживают такого же внимания. Главное правило: то, что вы добавляете в контракт, должно быть необязательным, и всё, что было необязательным, должно оставаться необязательным.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/api-versioning-should-be-your-last-resort
Версионирование API Должно Быть Последним Средством. Продолжение
Начало
Правила совместимости
- Сохраняйте существующие поля и поведение;
- Не превращайте необязательные данные запроса в обязательные;
- Не меняйте то, что делает существующая операция;
- Делайте всё новое в добавление и необязательным по умолчанию.
1. Добавляйте, а не заменяйте
Самое безопасное изменение обычно является аддитивным. Допустим, первоначальный ответ от
GET /orders/{id} был таким:{
"id": "ord_123",
"status": "paid",
"total": 100
}Вместо замены поля
total добавим новое:{
"id": "ord_123",
"status": "paid",
"total": 100,
"totalMoney": {
"amount": 100,
"currency": "USD"
}
}Существующие клиенты продолжают использовать
total. Новые могут перейти на totalMoney. Помечаем старое поле как устаревшее и удалим его только после реального периода миграции. Иногда некрасивый контракт — это цена совместимости.2. Делайте клиентов толерантными
Хорошо работающий клиент не должен выдавать ошибку из-за того, что сервер добавил поле, которое он не понимает. Если ответ изменился с:
{
"id": "ord_123",
"status": "paid"
}на:
{
"id": "ord_123",
"status": "paid",
"estimatedDeliveryDate": "2026-05-29"
}существующие клиенты должны игнорировать дополнительное свойство и продолжать работу.
В System.Text.Json неизвестные свойства игнорируются по умолчанию. Реальный риск обычно заключается в чрезмерно строгой проверке JSON (об этом позже на канале). Это одна из распространённых проблем. Команды заявляют о желании обратной совместимости, а затем генерируют клиентские модели, которые отклоняют любое неожиданное поле в ответе. Клиенты должны быть достаточно толерантными, чтобы игнорировать то, чего они не понимают.
3. Не меняйте то, что делает существующая операция
Самые опасные критические изменения скрываются в поведении. URL, тело запроса, формат ответа те же. Но то, что делает операция на сервере, отличается. Например,
DELETE /orders/{id} сначала реализовывал мягкое удаление, и заказ переходил в «архив», но по-прежнему отображался в отчётах аудита и мог быть восстановлен службой поддержки. Затем команда решила «почистить базу» и изменить поведение на жёсткое удаление. Ни один клиент сразу этого не заметит, но данные теперь «по-тихому» уничтожаются.Аналогично:
-
POST /orders раньше был идемпотентным, а затем незаметно перестал им быть;-
POST /orders/{id}/cancel раньше автоматически возвращал деньги, а затем перестал это делать, потому что «возвраты должны быть отдельным вызовом»;-
PUT /orders/{id} раньше был полной заменой, а теперь стал частичным слиянием;- Веб-хук раньше срабатывал один раз для каждого заказа, а теперь срабатывает для каждой позиции заказа;
и т.п.
Каждый из этих вариантов сохраняет стабильность URL, метода и структуры JSON, но нарушает все существующие интеграции таким образом, что это не будет видно при сравнении схем. Безопасный шаг тот же: добавлять, а не изменять. Например, параметр в конечной точке для жёсткого удаления
DELETE /orders/{id}?purge=trueКак только операция выпущена, её поведение становится частью контракта. Вы можете добавлять новые операции, можете объявить её устаревшей, но не можете незаметно изменять её работу.
4. Будьте осторожны с валидацией
Существует два варианта одной и той же ошибки:
- Сделать обязательным существующее необязательное поле;
- Добавить новое обязательное поле.
Оба варианта ломают существующих клиентов. Путь к конечной точке не меняется, но запросы, которые раньше выполнялись успешно, теперь отклоняются. Более безопасный путь — определить значения по умолчанию или ввести новую операцию для более строгого рабочего процесса.
Изменения в ответах обычно тщательно проверяются с точки зрения проектирования. Изменения в проверке запросов заслуживают такого же внимания. Главное правило: то, что вы добавляете в контракт, должно быть необязательным, и всё, что было необязательным, должно оставаться необязательным.
Окончание следует…
Источник: https://www.milanjovanovic.tech/blog/api-versioning-should-be-your-last-resort
👍5
День 2703. #ЗаметкиНаПолях
Версионирование API Должно Быть Последним Средством. Окончание
Начало
Продолжение
Новая операция часто дешевле, чем новая версия
Иногда сценарий использования действительно меняется настолько, что добавление новых флагов и необязательных параметров к существующей конечной точке становится запутанным.
Вот плохой путь:
Тут нет одной чистой операции, есть несколько рабочих процессов, скрывающихся за одной конечной точкой.
В этом случае лучше добавить новую операцию или ресурс. Это сохраняет стабильность старого контракта, предоставляя новому поведению чистое место. Например:
-
-
Это обычно намного дешевле, чем создание
Устаревание по-настоящему
Это недостающая часть управления изменениями API. Большинство случаев устаревания — это обман. Они существуют в документации, но ничего не происходит в процессе работы. Реальный процесс устаревания должен включать:
- Пометку старого поля или конечной точки как устаревшей в описании OpenAPI;
- Сообщение об устаревании во время выполнения;
- Предоставление пользователям пути миграции;
- Измерение фактического использования перед удалением чего-либо.
Если вы используете HTTP, то сообщения во время выполнения могут быть в заголовке ответа:
Теперь информация об устаревании видна в документации, видна в реальном трафике и связана с фактическим руководством по миграции.
И вот здесь телеметрия имеет значение. Если вы не знаете, какие клиенты всё ещё используют устаревшее поле или конечную точку, вы не управляете изменениями. Вы гадаете. Отслеживайте использование по ID клиента, ключу API, тенанту или имени приложения. Затем дождитесь, пока использование фактически исчезнет, прежде чем что-либо удалять.
Когда версионирование - правильное решение:
- старая и новая семантика не могут безопасно сосуществовать;
- модель ресурсов изменилась коренным образом;
- правила совместимости приводят к контракту, в котором никто не может разобраться.
А обдуманное версионирование означает выбор наименьшего возможного нарушения, которое вы можете оправдать. Иногда это новая форма конечной точки. Иногда это вариант представления. Иногда, особенно для публичных API, это прямое версионирование URL-адресов, поскольку оно явное и легко объяснимое.
Если вы используете версионирование, сочетайте его с реальным процессом устаревания (см. выше). Настоящая работа не в создании версии 2, а в том, чтобы перевести потребителей с версии 1.
Итого
Разрабатывайте контракты с учётом возможности их развития. Рассматривайте клиентов как долгосрочные интеграции, а не просто как сегодняшний код. И оставляйте версионирование для случаев, когда возможности совместимости действительно исчерпаны.
Источник: https://www.milanjovanovic.tech/blog/api-versioning-should-be-your-last-resort
Версионирование API Должно Быть Последним Средством. Окончание
Начало
Продолжение
Новая операция часто дешевле, чем новая версия
Иногда сценарий использования действительно меняется настолько, что добавление новых флагов и необязательных параметров к существующей конечной точке становится запутанным.
Вот плохой путь:
POST /orders?validateOnly=true&includeTaxEstimate=true&reserveInventory=true
Тут нет одной чистой операции, есть несколько рабочих процессов, скрывающихся за одной конечной точкой.
В этом случае лучше добавить новую операцию или ресурс. Это сохраняет стабильность старого контракта, предоставляя новому поведению чистое место. Например:
-
POST /orders остаётся простой конечной точкой «разместить заказ».-
POST /orders/quote становится операцией «скажите, сколько это будет стоить».Это обычно намного дешевле, чем создание
/v2/orders и перенос всего остального API вместе с этим.Устаревание по-настоящему
Это недостающая часть управления изменениями API. Большинство случаев устаревания — это обман. Они существуют в документации, но ничего не происходит в процессе работы. Реальный процесс устаревания должен включать:
- Пометку старого поля или конечной точки как устаревшей в описании OpenAPI;
- Сообщение об устаревании во время выполнения;
- Предоставление пользователям пути миграции;
- Измерение фактического использования перед удалением чего-либо.
Если вы используете HTTP, то сообщения во время выполнения могут быть в заголовке ответа:
Deprecation: true
Sunset: Wed, 31 Dec 2026 23:59:59 GMT
Link: <https://docs.example.com/migrations/orders-total>; rel="deprecation"
Теперь информация об устаревании видна в документации, видна в реальном трафике и связана с фактическим руководством по миграции.
И вот здесь телеметрия имеет значение. Если вы не знаете, какие клиенты всё ещё используют устаревшее поле или конечную точку, вы не управляете изменениями. Вы гадаете. Отслеживайте использование по ID клиента, ключу API, тенанту или имени приложения. Затем дождитесь, пока использование фактически исчезнет, прежде чем что-либо удалять.
Когда версионирование - правильное решение:
- старая и новая семантика не могут безопасно сосуществовать;
- модель ресурсов изменилась коренным образом;
- правила совместимости приводят к контракту, в котором никто не может разобраться.
А обдуманное версионирование означает выбор наименьшего возможного нарушения, которое вы можете оправдать. Иногда это новая форма конечной точки. Иногда это вариант представления. Иногда, особенно для публичных API, это прямое версионирование URL-адресов, поскольку оно явное и легко объяснимое.
Если вы используете версионирование, сочетайте его с реальным процессом устаревания (см. выше). Настоящая работа не в создании версии 2, а в том, чтобы перевести потребителей с версии 1.
Итого
Разрабатывайте контракты с учётом возможности их развития. Рассматривайте клиентов как долгосрочные интеграции, а не просто как сегодняшний код. И оставляйте версионирование для случаев, когда возможности совместимости действительно исчерпаны.
Источник: https://www.milanjovanovic.tech/blog/api-versioning-should-be-your-last-resort
👍2
Когда вы уже Senior или Team Lead, все больше времени уходит на планирование, синки, оценку задач и помощь другим. При этом код и разговоры про то, как все устроено под капотом, иногда отходят на второй план.
DotNext 2026 — это возможность на пару дней вернуться в эту часть разработки: обсуждать архитектуру, разбирать сложные инженерные решения и снова погружаться в .NET с коллегами, которым интересны те же вопросы.
В кулуарах встречаются разработчики разных уровней — от Middle до Senior: сравнивают подходы, делятся опытом и обсуждают задачи, которые редко помещаются в обычный рабочий созвон.
На сцене — доклады для тех, кому интересно разбираться глубже: от низкоуровневых оптимизаций и внутренних механизмов .NET до практических решений, которые можно забрать в свой стек.
Если хочется на пару дней сменить контекст и снова поговорить про инженерные вопросы с людьми своего уровня — самое время запланировать поездку. Билет можно приобрести на сайте.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4👎1
День 2703. #ЧтоНовенького
Вышел 5й Превью .NET 11
На прошлой неделе Microsoft выпустили 5ю превью версию .NET 11, которая включает обновления среды выполнения, SDK, библиотек, ASP.NET Core, .NET MAUI, C# и Entity Framework Core.
.NET SDK
1. Ряд улучшений для однофайловых приложений. Разработчики теперь могут разделять код между файлами, используя новую директиву #:ref, которая ссылается на другое однофайловое приложение как на библиотеку, без создания проекта:
Больше инструментов командной строки, включая команды package и NuGet, теперь понимают пути к однофайловым приложениям.
2. Добавлен шаблон сервера Model Context Protocol, поэтому
3. Проекты могут включить проверку, предупреждающую о наличии известных уязвимостей в установленном SDK или о завершении его поддержки:
4. Консольные и сервисные приложения, ориентированные на net11.0, теперь автоматически включают System.Net.Http.Json при включении неявных операторов using.
C#
1. Закрытый класс, который может быть производным только от класса в рамках одной сборки, что позволяет компилятору проверять, обрабатывает ли switch все случаи:
2. Новое объявление объединения создаёт тип значения, который содержит один из фиксированного набора типов вариантов, с поддержкой сопоставления с образцом:
Blazor
1. Теперь формы поддерживают мгновенную валидацию данных на стороне клиента без обращения к серверу, а также добавлена поддержка асинхронных правил проверки, таких как поиск в базе данных.
2. Сообщения валидации и имена свойств теперь могут быть локализованы.
3. Сортировка и пагинация QuickGrid теперь работают на статически отображаемых страницах.
4. Автономные приложения Blazor WebAssembly получили новый сервер разработки под названием Gateway, который добавляет встроенную маршрутизацию для SPA.
Entity Framework Core
1. Добавлена поддержка однофайловых приложений в инструменте dotnet ef и файл конфигурации для хранения параметров по умолчанию.
2. Новое предупреждение анализатора EF1004 указывает на асинхронные запросы, которые выполняются синхронно.
3. Совместимость с SQL Server 2022 теперь является стандартной, а сгенерированный код C# использует пространства имен с областью видимости файла.
Источник: https://www.infoq.com/news/2026/06/dotnet-11-preview-5/
Вышел 5й Превью .NET 11
На прошлой неделе Microsoft выпустили 5ю превью версию .NET 11, которая включает обновления среды выполнения, SDK, библиотек, ASP.NET Core, .NET MAUI, C# и Entity Framework Core.
.NET SDK
1. Ряд улучшений для однофайловых приложений. Разработчики теперь могут разделять код между файлами, используя новую директиву #:ref, которая ссылается на другое однофайловое приложение как на библиотеку, без создания проекта:
// app.cs
#:ref lib.cs
Console.WriteLine(MyLib.Greeter.Greet("World"));
// lib.cs
namespace MyLib;
public static class Greeter
{
public static string Greet(string name)
=> $"Hello, {name}!";
}
Больше инструментов командной строки, включая команды package и NuGet, теперь понимают пути к однофайловым приложениям.
2. Добавлен шаблон сервера Model Context Protocol, поэтому
dotnet new mcpserver работает без отдельного пакета. 3. Проекты могут включить проверку, предупреждающую о наличии известных уязвимостей в установленном SDK или о завершении его поддержки:
<PropertyGroup>
<CheckSdkVulnerabilities>true</CheckSdkVulnerabilities>
</PropertyGroup>
4. Консольные и сервисные приложения, ориентированные на net11.0, теперь автоматически включают System.Net.Http.Json при включении неявных операторов using.
C#
1. Закрытый класс, который может быть производным только от класса в рамках одной сборки, что позволяет компилятору проверять, обрабатывает ли switch все случаи:
public closed record class GateState;
public record class Closed : GateState;
public record class Open(float Percent) : GateState;
static string Describe(GateState state)
=> state switch
{
Closed => "closed",
Open(var percent) => $"{percent}% open"
};
2. Новое объявление объединения создаёт тип значения, который содержит один из фиксированного набора типов вариантов, с поддержкой сопоставления с образцом:
public record class Dog(string Name);
public record class Cat(int Lives);
public union Pet(Dog, Cat);
static string Describe(Pet pet)
=> pet switch
{
Dog(var name) => $"dog: {name}",
Cat(var lives) => $"cat: {lives}"
};
Blazor
1. Теперь формы поддерживают мгновенную валидацию данных на стороне клиента без обращения к серверу, а также добавлена поддержка асинхронных правил проверки, таких как поиск в базе данных.
2. Сообщения валидации и имена свойств теперь могут быть локализованы.
3. Сортировка и пагинация QuickGrid теперь работают на статически отображаемых страницах.
4. Автономные приложения Blazor WebAssembly получили новый сервер разработки под названием Gateway, который добавляет встроенную маршрутизацию для SPA.
Entity Framework Core
1. Добавлена поддержка однофайловых приложений в инструменте dotnet ef и файл конфигурации для хранения параметров по умолчанию.
2. Новое предупреждение анализатора EF1004 указывает на асинхронные запросы, которые выполняются синхронно.
3. Совместимость с SQL Server 2022 теперь является стандартной, а сгенерированный код C# использует пространства имен с областью видимости файла.
Источник: https://www.infoq.com/news/2026/06/dotnet-11-preview-5/
👍2
🚀 DDD на C#: от теории к микросервису за 6 недель
Если ты пишешь на C# и в какой-то момент начал чувствовать, что вроде всё работает, но как-то костыльно — это тревожный сигнал.
Новая фича затрагивает десятки файлов. Тесты становятся сложнее самого кода. Любое изменение заставляет переживать, что сломается что-то ещё.
Обычно проблема не в разработчиках. Проблема в том, что проект растёт без понятной архитектурной модели.
На курсе по Domain-Driven Design и Clean Architecture на C# ты научишься:
— Отделять бизнес-логику от инфраструктуры
— Организовывать код так, чтобы новые требования не приводили к переписыванию половины сервиса
— Писать тесты, которые проверяют поведение системы, а не набор моков
— Подключать HTTP, gRPC и Kafka без изменений в доменной логике
— Строить сервисы, которые проще поддерживать и развивать
За 6 недель ты соберёшь полноценный микросервис на C# с DDD, Kafka, gRPC и Clean Architecture на реальном кейсе диспетчеризации заказов.
👨🏫 Автор курса — Кирилл Ветчинкин, архитектор Авито, ex Staff Engineer Купер, ex Head of Backend BCS Broker.
🎁 Первый модуль доступен бесплатно.
В нём разберём, почему кодовые базы со временем становятся хрупкими, откуда появляются сложные тесты и постоянный страх изменений, и как DDD и Clean Architecture помогают решить эти проблемы на практике.
Посмотри демо-модуль и оцени, насколько этот подход подходит для твоих проектов:
https://microarch.ru/courses/ddd/languages/csharp?utm_source=posev&utm_medium=erid:2VtzqwrtmqB&utm_campaign=1
Реклама. ИП Ветчинкин К.Е. ИНН: 773376451099 Erid: 2VtzqwrtmqB
#реклама
Если ты пишешь на C# и в какой-то момент начал чувствовать, что вроде всё работает, но как-то костыльно — это тревожный сигнал.
Новая фича затрагивает десятки файлов. Тесты становятся сложнее самого кода. Любое изменение заставляет переживать, что сломается что-то ещё.
Обычно проблема не в разработчиках. Проблема в том, что проект растёт без понятной архитектурной модели.
На курсе по Domain-Driven Design и Clean Architecture на C# ты научишься:
— Отделять бизнес-логику от инфраструктуры
— Организовывать код так, чтобы новые требования не приводили к переписыванию половины сервиса
— Писать тесты, которые проверяют поведение системы, а не набор моков
— Подключать HTTP, gRPC и Kafka без изменений в доменной логике
— Строить сервисы, которые проще поддерживать и развивать
За 6 недель ты соберёшь полноценный микросервис на C# с DDD, Kafka, gRPC и Clean Architecture на реальном кейсе диспетчеризации заказов.
👨🏫 Автор курса — Кирилл Ветчинкин, архитектор Авито, ex Staff Engineer Купер, ex Head of Backend BCS Broker.
🎁 Первый модуль доступен бесплатно.
В нём разберём, почему кодовые базы со временем становятся хрупкими, откуда появляются сложные тесты и постоянный страх изменений, и как DDD и Clean Architecture помогают решить эти проблемы на практике.
Посмотри демо-модуль и оцени, насколько этот подход подходит для твоих проектов:
https://microarch.ru/courses/ddd/languages/csharp?utm_source=posev&utm_medium=erid:2VtzqwrtmqB&utm_campaign=1
Реклама. ИП Ветчинкин К.Е. ИНН: 773376451099 Erid: 2VtzqwrtmqB
#реклама
👎2
День 2704. #ЗаметкиНаПолях
Ужесточаем Десериализацию JSON в System.Text.Json. Начало
Рассмотрим следующий JSON:
Два свойства с одинаковым именем. В разделе 4 RFC 8259 говорится, что имена объектов "ДОЛЖНЫ быть уникальными", и предупреждается, что поведение парсера непредсказуемо, если они таковыми не являются. System.Text.Json выбирает разрешительный путь: побеждает последняя запись, без предупреждений, без ошибок.
Значение, присвоенное злоумышленником, незаметно побеждает. Это не просто особенность дублирования свойств. Десериализация по умолчанию также игнорирует дополнительные поля, которые может внедрить злоумышленник, позволяет null проникать в свойства, не допускающие null, и пропускает отсутствующие обязательные данные. Каждое из этих «удобств» представляет потенциальную уязвимость.
JsonSerializerOptions.Strict
.NET 10 представляет JsonSerializerOptions.Strict, новую настройку наряду с Default и Web. Default отдаёт приоритет обратной совместимости, Web оптимизирует работу для типичных HTTP API, а Strict следует лучшим практикам безопасности:
В каждой настройке 5 свойств. Рассмотрим их значения, и на что они влияют.
1. AllowDuplicateProperties (Принятие дублирующих свойств)
- Default: true
- Web: true
- Strict: false
Протоколы, использующие многоуровневый анализ JSON (OAuth 2.0, OpenID Connect, подписи веб-хуков), могут быть взломаны, если разные парсеры обрабатывают дублирующиеся входные данные по-разному. Один парсер видит первое значение, другой — последнее.
Эта защита распространяется не только на десериализацию обычных C# объектов. Также можно обнаруживать дубликаты в JsonDocument, JsonNode и Dictionary<string, T>.
2. UnmappedMemberHandling (Обработка несоответствующих членов)
- Default: Skip
- Web: Skip
- Strict: Disallow
Десериализация по умолчанию молча отбрасывает свойства JSON, которые не соответствуют вашему типу .NET. Это удобно во время разработки. Но это опасно на границе доверия.
Это позволяет выявлять класс ошибок, когда клиенты отправляют поля, которые API не должен принимать. Если вы их не отклоняете, вы не знаете об их наличии. UnmappedMemberHandling добавлено в .NET 8.
3. PropertyNameCaseInsensitive (Учёт регистра свойств)
- Default: false
- Web: true
- Strict: false
Web устанавливает свойство в true, чтобы "username" соответствовало свойству C# с именем Username. Это полезно для типичного взаимодействия JavaScript с .NET. Но в сочетании с UnmappedMemberHandling.Disallow в режиме Strict учёт регистра становится точным контрактом: имена свойств JSON должны точно совпадать с именами свойств C#, иначе десериализатор их отклонит.
Если ваши клиенты отправляют данные в формате camelCase, и вам нужна строгая проверка, добавьте
4. RespectNullableAnnotations (Учёт nullable-аннотаций)
- Default: false
- Web: false
- Strict: true
Обнуляемые ссылочные типы C# помогают выявлять проблемы с null на этапе компиляции. Но System.Text.Json по умолчанию игнорирует их во время десериализации. Значение null в JSON легко помещается в строковое свойство без каких-либо проблем. RespectNullableAnnotations добавлено в .NET 9. В строгом режиме сериализатор обеспечивает соблюдение ваших nullable-аннотаций на границе десериализации. Если вы объявили string Name (а не string? Name), сериализатор будет придерживаться этого правила.
5. RespectRequiredConstructorParameters (Учёт обязательных параметров конструктора)
- Default: false
- Web: false
- Strict: true
В типах записей и классах с параметризованными конструкторами обязательные параметры могут быть незаметно заполнены значениями по умолчанию, если в JSON отсутствуют данные.
Это позволяет выявлять частичные данные, которые в противном случае привели бы к тому, что объекты оказались бы в недопустимом состоянии. В сочетании с обеспечением соблюдения nullable-аннотаций это означает, что ваши десериализованные объекты соответствуют объявленному контракту вашего типа.
Окончание следует…
Источник: https://duendesoftware.com/blog/20260430-harden-your-dotnet-json-deserialization
Ужесточаем Десериализацию JSON в System.Text.Json. Начало
Рассмотрим следующий JSON:
{"Amount": 100, "Amount": -999}Два свойства с одинаковым именем. В разделе 4 RFC 8259 говорится, что имена объектов "ДОЛЖНЫ быть уникальными", и предупреждается, что поведение парсера непредсказуемо, если они таковыми не являются. System.Text.Json выбирает разрешительный путь: побеждает последняя запись, без предупреждений, без ошибок.
Значение, присвоенное злоумышленником, незаметно побеждает. Это не просто особенность дублирования свойств. Десериализация по умолчанию также игнорирует дополнительные поля, которые может внедрить злоумышленник, позволяет null проникать в свойства, не допускающие null, и пропускает отсутствующие обязательные данные. Каждое из этих «удобств» представляет потенциальную уязвимость.
JsonSerializerOptions.Strict
.NET 10 представляет JsonSerializerOptions.Strict, новую настройку наряду с Default и Web. Default отдаёт приоритет обратной совместимости, Web оптимизирует работу для типичных HTTP API, а Strict следует лучшим практикам безопасности:
using System.Text.Json;
string json = """{"Amount": 100, "Amount": -999}""";
try
{
JsonSerializer.Deserialize<Payment>(json,
JsonSerializerOptions.Strict);
}
catch (JsonException ex)
{
Console.WriteLine(ex.Message);
}
// Вывод: Duplicate property 'Amount' encountered during deserialization of type 'Payment'.
public record Payment(int Amount);
В каждой настройке 5 свойств. Рассмотрим их значения, и на что они влияют.
1. AllowDuplicateProperties (Принятие дублирующих свойств)
- Default: true
- Web: true
- Strict: false
Протоколы, использующие многоуровневый анализ JSON (OAuth 2.0, OpenID Connect, подписи веб-хуков), могут быть взломаны, если разные парсеры обрабатывают дублирующиеся входные данные по-разному. Один парсер видит первое значение, другой — последнее.
Эта защита распространяется не только на десериализацию обычных C# объектов. Также можно обнаруживать дубликаты в JsonDocument, JsonNode и Dictionary<string, T>.
2. UnmappedMemberHandling (Обработка несоответствующих членов)
- Default: Skip
- Web: Skip
- Strict: Disallow
Десериализация по умолчанию молча отбрасывает свойства JSON, которые не соответствуют вашему типу .NET. Это удобно во время разработки. Но это опасно на границе доверия.
Это позволяет выявлять класс ошибок, когда клиенты отправляют поля, которые API не должен принимать. Если вы их не отклоняете, вы не знаете об их наличии. UnmappedMemberHandling добавлено в .NET 8.
3. PropertyNameCaseInsensitive (Учёт регистра свойств)
- Default: false
- Web: true
- Strict: false
Web устанавливает свойство в true, чтобы "username" соответствовало свойству C# с именем Username. Это полезно для типичного взаимодействия JavaScript с .NET. Но в сочетании с UnmappedMemberHandling.Disallow в режиме Strict учёт регистра становится точным контрактом: имена свойств JSON должны точно совпадать с именами свойств C#, иначе десериализатор их отклонит.
Если ваши клиенты отправляют данные в формате camelCase, и вам нужна строгая проверка, добавьте
[JsonPropertyName("username")] к свойству. Так, контракт будет явно указан в определении типа, а не неявно в параметрах.4. RespectNullableAnnotations (Учёт nullable-аннотаций)
- Default: false
- Web: false
- Strict: true
Обнуляемые ссылочные типы C# помогают выявлять проблемы с null на этапе компиляции. Но System.Text.Json по умолчанию игнорирует их во время десериализации. Значение null в JSON легко помещается в строковое свойство без каких-либо проблем. RespectNullableAnnotations добавлено в .NET 9. В строгом режиме сериализатор обеспечивает соблюдение ваших nullable-аннотаций на границе десериализации. Если вы объявили string Name (а не string? Name), сериализатор будет придерживаться этого правила.
5. RespectRequiredConstructorParameters (Учёт обязательных параметров конструктора)
- Default: false
- Web: false
- Strict: true
В типах записей и классах с параметризованными конструкторами обязательные параметры могут быть незаметно заполнены значениями по умолчанию, если в JSON отсутствуют данные.
Это позволяет выявлять частичные данные, которые в противном случае привели бы к тому, что объекты оказались бы в недопустимом состоянии. В сочетании с обеспечением соблюдения nullable-аннотаций это означает, что ваши десериализованные объекты соответствуют объявленному контракту вашего типа.
Окончание следует…
Источник: https://duendesoftware.com/blog/20260430-harden-your-dotnet-json-deserialization
👍5
День 2705. #ЗаметкиНаПолях
Ужесточаем Десериализацию JSON в System.Text.Json. Окончание
Начало
Строгий режим и генераторы кода
В генераторах кода нужно вручную настроить все параметры в JsonSourceGenerationOptionsAttribute:
Сокращенного обозначения параметра Strict не существует. Каждое свойство устанавливается индивидуально.
Строгий режим в минимальных API ASP.NET Core
В веб-приложении параметры JSON настраиваются один раз, и каждая конечная точка наследует их:
Фреймворк перехватывает исключение JsonException во время привязки модели и возвращает ошибку 400 Bad Request с подробным описанием проблемы. Ваш код конечной точки видит только действительные, полностью привязанные объекты.
Параметры для каждой конечной точки
Если требуется строгая проверка для одних конечных точек, но более мягкий анализ для других, используйте Results.Json с явными параметрами:
Для десериализации можно брать чистое тело запроса:
Когда использовать Strict
На границах доверия. Конечные точки токенов, контроллеры API — всё, где вы принимаете JSON от клиента, которого вы не полностью контролируете. Цена — исключение JsonException, когда полезная нагрузка не соответствует вашему контракту.
Если вы получаете JSON от сторонних API с несогласованными схемами, строгий режим отклонит данные, которые вы, возможно, захотите обработать корректно. В таких случаях используйте режим Default или Web и проверяйте после десериализации.
Не нужно сразу переводить всё на строгий режим. Начните с наиболее рискованных конечных точек. Перехватывайте исключения JsonException, регистрируйте их, исправляйте вызывающие стороны, отправляющие несоответствующие данные.
Параметр Strict проверяет структурные нарушения контракта. Он не защищает от глубоко вложенного JSON (используйте MaxDepth), слишком больших объёмов данных (обрабатывайте на уровне HTTP с ограничениями размера запроса) или путаницы полиморфных типов. Строгий режим — это один уровень защиты, а не единственный.
Укрепление вашей безопасности
Каждая конечная точка API, принимающая JSON, является границей доверия. Разрешительная десериализация делает эту границу уязвимой. Режим Strict не добавляет новую логику проверки. Он активирует защиту, которая уже существует в System.Text.Json, но отключена по умолчанию для обратной совместимости.
Источник: https://duendesoftware.com/blog/20260430-harden-your-dotnet-json-deserialization
Ужесточаем Десериализацию JSON в System.Text.Json. Окончание
Начало
Строгий режим и генераторы кода
В генераторах кода нужно вручную настроить все параметры в JsonSourceGenerationOptionsAttribute:
[JsonSourceGenerationOptions(
AllowDuplicateProperties = false,
UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow,
PropertyNameCaseInsensitive = false,
RespectNullableAnnotations = true,
RespectRequiredConstructorParameters = true
)]
Сокращенного обозначения параметра Strict не существует. Каждое свойство устанавливается индивидуально.
Строгий режим в минимальных API ASP.NET Core
В веб-приложении параметры JSON настраиваются один раз, и каждая конечная точка наследует их:
builder.Services.ConfigureHttpJsonOptions(o =>
{
o.SerializerOptions.AllowDuplicateProperties = false;
o.SerializerOptions.UnmappedMemberHandling =
JsonUnmappedMemberHandling.Disallow;
o.SerializerOptions.PropertyNameCaseInsensitive = false;
o.SerializerOptions.RespectNullableAnnotations = true;
o.SerializerOptions.RespectRequiredConstructorParameters = true;
});
// …
var app = builder.Build();
app.MapPost("/payments", (Payment pmt) =>
{
// Если тело запроса нарушает какое-либо
// из требований сериализатора, фреймворк
// вернёт 400 Bad Request без выполнения тела метода
return Results.Ok(pmt);
});
app.Run();
Фреймворк перехватывает исключение JsonException во время привязки модели и возвращает ошибку 400 Bad Request с подробным описанием проблемы. Ваш код конечной точки видит только действительные, полностью привязанные объекты.
Параметры для каждой конечной точки
Если требуется строгая проверка для одних конечных точек, но более мягкий анализ для других, используйте Results.Json с явными параметрами:
app.MapGet("/api/data", () =>
Results.Json(new { message = "Hello" }, JsonSerializerOptions.Strict));Для десериализации можно брать чистое тело запроса:
app.MapPost("/api/strict",
async (HttpContext context) =>
{
var pmt = await context.Request
.ReadFromJsonAsync<Payment>(JsonSerializerOptions.Strict);
return Results.Ok(pmt);
});Когда использовать Strict
На границах доверия. Конечные точки токенов, контроллеры API — всё, где вы принимаете JSON от клиента, которого вы не полностью контролируете. Цена — исключение JsonException, когда полезная нагрузка не соответствует вашему контракту.
Если вы получаете JSON от сторонних API с несогласованными схемами, строгий режим отклонит данные, которые вы, возможно, захотите обработать корректно. В таких случаях используйте режим Default или Web и проверяйте после десериализации.
Не нужно сразу переводить всё на строгий режим. Начните с наиболее рискованных конечных точек. Перехватывайте исключения JsonException, регистрируйте их, исправляйте вызывающие стороны, отправляющие несоответствующие данные.
Параметр Strict проверяет структурные нарушения контракта. Он не защищает от глубоко вложенного JSON (используйте MaxDepth), слишком больших объёмов данных (обрабатывайте на уровне HTTP с ограничениями размера запроса) или путаницы полиморфных типов. Строгий режим — это один уровень защиты, а не единственный.
Укрепление вашей безопасности
Каждая конечная точка API, принимающая JSON, является границей доверия. Разрешительная десериализация делает эту границу уязвимой. Режим Strict не добавляет новую логику проверки. Он активирует защиту, которая уже существует в System.Text.Json, но отключена по умолчанию для обратной совместимости.
Источник: https://duendesoftware.com/blog/20260430-harden-your-dotnet-json-deserialization
👍1
День 2706. #ВопросыНаСобеседовании
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
37. Аутентификация и авторизация
«Как бы вы реализовали аутентификацию и авторизацию в приложении ASP.NET Core, используя минимальные API? Приведите пример настройки этих мер безопасности и их применения для обеспечения безопасного доступа к данным».
Хороший ответ
Реализация аутентификации и авторизации в приложении минимальных API ASP.NET включает в себя несколько важных шагов, которые гарантируют, что доступ к определённым функциям будут иметь только аутентифицированные и авторизованные пользователи. Вот как бы я подошел к этому.
Начнём с настройки необходимых сервисов в
Далее определим конечные точки минимального API. Используем настроенные сервисы для защиты этих конечных точек:
В этом примере атрибут Authorize используется в определении маршрутов минимального API, что гарантирует соблюдение конечной точкой указанной политики авторизации.
Настройка аутентификации на основе JWT и определение чётких политик авторизации эффективно защищает конечные точки от несанкционированного доступа.
Часто встречающийся плохой ответ
«Можно использовать базовую аутентификацию (HTTP-заголовок `Authorization: Basic …`) для каждого запроса, проверяя имена пользователей и пароли по БД непосредственно в каждой конечной точке API. Так мы гарантируем аутентификацию каждого вызова без усложнения ситуации с помощью JWT или внешних поставщиков.»
Почему это неправильно
- Риски безопасности и масштабируемость: такой подход подвергает приложение многочисленным рискам безопасности, таким как перехват учётных данных и атаки методом перебора. Кроме того, прямая обработка аутентификации в каждой конечной точке может привести к непоследовательным реализациям и затруднить масштабирование и обслуживание.
- Неправильная практика обеспечения безопасности: опора на базовую аутентификацию без защищённых протоколов или централизованных систем управления идентификацией не позволяет использовать более безопасные, масштабируемые и гибкие подходы, такие как OAuth и JWT.
- Отсутствие управления доступом на основе ролей: в ответе не рассматривается управление доступом на основе ролей или детальные разрешения, которые имеют решающее значение для более крупных приложений, где разные пользователи имеют разные уровни доступа.
Эта ошибка часто возникает из-за недостаточного понимания современных механизмов аутентификации или неправильного понимания лучших практик безопасности в веб-разработке. Она отражает необходимость более глубокого изучения того, как безопасно управлять пользователями и правами доступа в корпоративных приложениях.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
Марк Прайс предложил свой набор из 60 вопросов (как технических, так и на софт-скилы), которые могут задать на собеседовании.
37. Аутентификация и авторизация
«Как бы вы реализовали аутентификацию и авторизацию в приложении ASP.NET Core, используя минимальные API? Приведите пример настройки этих мер безопасности и их применения для обеспечения безопасного доступа к данным».
Хороший ответ
Реализация аутентификации и авторизации в приложении минимальных API ASP.NET включает в себя несколько важных шагов, которые гарантируют, что доступ к определённым функциям будут иметь только аутентифицированные и авторизованные пользователи. Вот как бы я подошел к этому.
Начнём с настройки необходимых сервисов в
Program.cs. Вот пример использования JWT (JSON Web Tokens) для аутентификации:var builder = WebApplication.CreateBuilder(args);
// Добавляем аутентификацию
builder.Services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", opts =>
{
opts.Authority = "https://authserver.com";
opts.TokenValidationParameters = new()
{ ValidateAudience = false };
});
// Добавляем авторизацию
builder.Services.AddAuthorization(opts =>
{
opts.AddPolicy("MustBeAdmin", policy =>
policy.RequireClaim("role", "Admin"));
});
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
Далее определим конечные точки минимального API. Используем настроенные сервисы для защиты этих конечных точек:
app.MapGet("/secure-data",
[Authorize(Policy="MustBeAdmin")] () =>
{
return "Доступно только админам";
});В этом примере атрибут Authorize используется в определении маршрутов минимального API, что гарантирует соблюдение конечной точкой указанной политики авторизации.
Настройка аутентификации на основе JWT и определение чётких политик авторизации эффективно защищает конечные точки от несанкционированного доступа.
Часто встречающийся плохой ответ
«Можно использовать базовую аутентификацию (HTTP-заголовок `Authorization: Basic …`) для каждого запроса, проверяя имена пользователей и пароли по БД непосредственно в каждой конечной точке API. Так мы гарантируем аутентификацию каждого вызова без усложнения ситуации с помощью JWT или внешних поставщиков.»
Почему это неправильно
- Риски безопасности и масштабируемость: такой подход подвергает приложение многочисленным рискам безопасности, таким как перехват учётных данных и атаки методом перебора. Кроме того, прямая обработка аутентификации в каждой конечной точке может привести к непоследовательным реализациям и затруднить масштабирование и обслуживание.
- Неправильная практика обеспечения безопасности: опора на базовую аутентификацию без защищённых протоколов или централизованных систем управления идентификацией не позволяет использовать более безопасные, масштабируемые и гибкие подходы, такие как OAuth и JWT.
- Отсутствие управления доступом на основе ролей: в ответе не рассматривается управление доступом на основе ролей или детальные разрешения, которые имеют решающее значение для более крупных приложений, где разные пользователи имеют разные уровни доступа.
Эта ошибка часто возникает из-за недостаточного понимания современных механизмов аутентификации или неправильного понимания лучших практик безопасности в веб-разработке. Она отражает необходимость более глубокого изучения того, как безопасно управлять пользователями и правами доступа в корпоративных приложениях.
Источник: https://github.com/markjprice/tools-skills-net8/blob/main/docs/interview-qa/readme.md
👎3
День 2707. #ЗаметкиНаПолях
Разбираем Сопоставления по Шаблону в C#. Часть 1/5
Сопоставление по шаблону позволяет проверить, имеет ли выражение определённую форму (тип, значение, структуру) и сразу извлечь из него данные. В последних версиях C# сопоставление по шаблону получило много изменений, и стало легко неправильно его использовать, поэтому разберём, когда его следует применять, а когда лучше нет.
TL;DR
Кратко рассмотрим последние появившиеся шаблоны и когда их использовать:
C#7+
1. Null -
2. Константа -
3. Тип/объявление -
4. Отброс/var -
C#8+
5. Выражение switch -
6. Шаблоны свойств -
7. Позиционные шаблоны -
8. Кортежи -
C#9+
9. Логические и, или, не -
10. Относительные шаблоны -
C#10+
11. Расширенные шаблоны свойств -
C#11+
12. Список/срез -
Что такое сопоставление по шаблону в C#?
Это языковая функция, которая проверяет, имеет ли выражение определённую форму — тип, константу или структуру — и, в случае совпадения, извлекает его данные в типизированные переменные.
Шаблон — описание того, как должно выглядеть выражение. Сопоставление — проверка выражения на соответствие этому описанию. Результатом является логическое значение (булево значение), и в случае успешного совпадения язык также может извлекать данные в типизированные переменные.
По сравнению с длинными цепочками операторов
- Проверяемое выражение используется один раз, а не несколько.
- Проверки типов предоставляют найденное значение как типизированную переменную. Никаких приведений типов.
- Выражения
Оба фрагмента кода ниже выполняют одну и ту же работу. Второй вариант сочетает в себе шаблоны типа и свойства, и разницу в читаемости трудно оспорить:
Шаблоны используются в трех местах:
- оператор
- классический оператор
- выражение
Продолжение следует…
Источник: https://blog.ndepend.com/c-pattern-matching-explained/
Разбираем Сопоставления по Шаблону в C#. Часть 1/5
Сопоставление по шаблону позволяет проверить, имеет ли выражение определённую форму (тип, значение, структуру) и сразу извлечь из него данные. В последних версиях C# сопоставление по шаблону получило много изменений, и стало легко неправильно его использовать, поэтому разберём, когда его следует применять, а когда лучше нет.
TL;DR
Кратко рассмотрим последние появившиеся шаблоны и когда их использовать:
C#7+
1. Null -
x is null – нужна проверка на null, избегая перегруженных операторов.2. Константа -
code is 404 - для сравнения значения с константой.3. Тип/объявление -
shape is Circle c - для неродственных типов.4. Отброс/var -
_ => … - ветка «по умолчанию» в switch, либо как заместитель в других шаблонах.C#8+
5. Выражение switch -
x switch { … } - нужно значение из switch с исчерпывающей проверкой.6. Шаблоны свойств -
{ Age: >= 18 } - решение зависит от свойства объекта.7. Позиционные шаблоны -
(var x, var y) - целевой тип имеет деконструктор или это запись.8. Кортежи -
(a, b) switch { … } - решение зависит от комбинации входных данных.C#9+
9. Логические и, или, не -
is not null - для композиции шаблонов без повтора переменной.10. Относительные шаблоны -
is >= 100 - проверка на числовые диапазоны.C#10+
11. Расширенные шаблоны свойств -
{ Address.City: "Paris" } - сопоставление со вложенными свойствами без вложенных скобок.C#11+
12. Список/срез -
arr is [1, .., 9] - шаблоны для небольших последовательностей.Что такое сопоставление по шаблону в C#?
Это языковая функция, которая проверяет, имеет ли выражение определённую форму — тип, константу или структуру — и, в случае совпадения, извлекает его данные в типизированные переменные.
Шаблон — описание того, как должно выглядеть выражение. Сопоставление — проверка выражения на соответствие этому описанию. Результатом является логическое значение (булево значение), и в случае успешного совпадения язык также может извлекать данные в типизированные переменные.
По сравнению с длинными цепочками операторов
if, сопоставление с образцом даёт 3 важных преимущества:- Проверяемое выражение используется один раз, а не несколько.
- Проверки типов предоставляют найденное значение как типизированную переменную. Никаких приведений типов.
- Выражения
switch обнаруживают пропущенные случаи во время компиляции.Оба фрагмента кода ниже выполняют одну и ту же работу. Второй вариант сочетает в себе шаблоны типа и свойства, и разницу в читаемости трудно оспорить:
// Традиционный оператор if с несколькими условиями и приведением типов
if (person != null && person.Age >= 18 && person is Employee
&& ((Employee)person).YearsAtCompany > 5)
…
// Аналогичное сопоставление по шаблону
if (person is Employee { Age: >= 18, YearsAtCompany: > 5 })
…
Шаблоны используются в трех местах:
- оператор
is,- классический оператор
switch,- выражение
switch, появившееся в C# 8.Продолжение следует…
Источник: https://blog.ndepend.com/c-pattern-matching-explained/
👍8