JoinRpg.Common.WebInfrastructure 2026.9.533

dotnet add package JoinRpg.Common.WebInfrastructure --version 2026.9.533
                    
NuGet\Install-Package JoinRpg.Common.WebInfrastructure -Version 2026.9.533
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="JoinRpg.Common.WebInfrastructure" Version="2026.9.533" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="JoinRpg.Common.WebInfrastructure" Version="2026.9.533" />
                    
Directory.Packages.props
<PackageReference Include="JoinRpg.Common.WebInfrastructure" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add JoinRpg.Common.WebInfrastructure --version 2026.9.533
                    
#r "nuget: JoinRpg.Common.WebInfrastructure, 2026.9.533"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package JoinRpg.Common.WebInfrastructure@2026.9.533
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=JoinRpg.Common.WebInfrastructure&version=2026.9.533
                    
Install as a Cake Addin
#tool nuget:?package=JoinRpg.Common.WebInfrastructure&version=2026.9.533
                    
Install as a Cake Tool

JoinRpg.Common.WebInfrastructure

Общая веб-инфраструктура для ASP.NET Core приложений JoinRpg: логирование (Serilog), OpenTelemetry, Data Protection, кеширование, health checks, поддержка PostgreSQL, логин через id.joinrpg.ru.

Подключение

1. Регистрация сервисов (Program.cs)

Метод над WebApplicationBuilder регистрирует сервисы платформы и заодно подключает Serilog (UseJoinSerilog) с тем же именем приложения:

builder.AddJoinWebPlatform(
    appName: "MyApp.Name",
    dataProtectionConnectionStringName: "DataProtection",
    telemetryServiceNames: ["MyApp.ActivitySourceName"]
);

Если у приложения одна база, ключи Data Protection можно хранить в его собственном DbContext — тогда отдельная строка подключения DataProtection не нужна. Контекст реализует IDataProtectionKeyContext (таблица DataProtectionKeys появится в его миграциях) и регистрируется как обычно через AddJoinEfCoreDbContext:

builder.AddJoinWebPlatform<MyDbContext>(
    appName: "MyApp.Name",
    telemetryServiceNames: ["MyApp.ActivitySourceName"]
);

Если WebApplicationBuilder под рукой нет (класс Startup), есть те же методы над IServiceCollection — им конфигурацию и окружение передают явно, а Serilog подключают отдельно: builder.Host.UseJoinSerilog("MyApp.Name").

Опционально — конфигурация имён хостов JoinRpg (нужна, если приложение использует JoinRpgHostNamesOptions):

services.Configure<JoinRpgHostNamesOptions>(
    Configuration.GetSection("JoinRpgHostNames")
);

2. Подключение DbContext (если нужно)

Регистрация DbContext с PostgreSQL и health check:

services.AddJoinEfCoreDbContext<MyDbContext>(
    Configuration, Environment,
    connectionStringName: "MyDatabase"
);

Для запуска миграций через Joinrpg.Dal.Migrate:

services.RegisterMigrator<MyDbContext>(
    Configuration, Environment,
    connectionStringName: "MyDatabase"
);

3. Логин через id.joinrpg.ru (OpenIddict client)

Для сателлитных сайтов, авторизующих пользователей через id.joinrpg.ru (authorization code flow):

services.AddJoinRpgAuthentication<MyDbContext>(Configuration);
services.AddScoped<IJoinUserLoginHandler, MyUserLoginHandler>();

MyDbContext — контекст, в котором OpenIddict хранит своё состояние (authorization/token records). В его OnModelCreating нужно вызвать modelBuilder.UseOpenIddict() (пакет OpenIddict.EntityFrameworkCore).

IJoinUserLoginHandler — приложение-специфичная часть логина: получив UserIdentification и внешний ClaimsPrincipal от id.joinrpg.ru, реализация должна создать/обновить локального пользователя и дополнить список claims, с которыми он будет залогинен по cookie-схеме:

internal class MyUserLoginHandler(MyDbContext dbContext) : IJoinUserLoginHandler
{
    public async Task HandleLoginAsync(UserIdentification userId, ClaimsPrincipal externalPrincipal, List<Claim> claims, CancellationToken cancellationToken)
    {
        // найти/создать локального пользователя по userId, дополнить claims
    }
}

В pipeline замаппить эндпойнты /login, /signin-joinrpg, /logout:

app.MapAuthEndpoints();

Конфигурация (секция JoinRpgOidc). Issuer по умолчанию — https://id.joinrpg.ru/, указывать нужно только для отличного от прода окружения (dev/staging):

{
  "JoinRpgOidc": {
    "ClientId": "my-app",
    "ClientSecret": "..."
  }
}

Получить id пользователя из текущего ClaimsPrincipal: user.GetJoinrpgUserId().

4. Middleware pipeline

// UseForwardedHeaders() вызывать не нужно — AddJoinWebPlatform сам ставит его в начало конвейера
app.UseOpenTelemetryPrometheusScrapingEndpoint();  // Эндпойнт /metrics
app.UseJoinRequestLogging();         // Логирование HTTP-запросов через Serilog

// ... остальной pipeline ...

app.MapJoinHealthChecks();           // Эндпойнты /health, /health/ready, /health/live

5. MVC/Razor Pages фильтры (опционально)

Для обогащения логов данными из MVC/Razor Pages контекста:

options.Filters.Add<SerilogMvcFilter>();
options.Filters.Add<SerilogRazorPagesFilter>();

Состав функционала

Компонент Класс/Метод Описание
Логирование UseJoinSerilog(), UseJoinRequestLogging() Serilog с обогащением (пользователь, трассировка, YC-уровни), JSON/text форматтеры, фильтрация health check запросов
Data Protection AddJoinDataProtection() (вызывается из AddJoinWebPlatform) Хранение ключей шифрования в PostgreSQL для работы в нескольких репликах: в отдельной базе или в DbContext приложения (AddJoinDataProtection<T>())
OpenTelemetry AddJoinOpenTelemetry() (вызывается из AddJoinWebPlatform) Distributed tracing + метрики, Prometheus endpoint /metrics. Экспортируются meter'ы с именем приложения (appName), общий JoinRpg, JoinRpg.WebInfrastructure и Microsoft.AspNetCore.Hosting
Кеширование PerRequestCache<K,V>, SingletonCache<K,V> Per-request scoped кеш и singleton-кеш с фабрикой значений
Health Checks MapJoinHealthChecks() Эндпойнты /health (полный), /health/ready (тег ready), /health/live (всегда OK)
robots.txt AddJoinRobotsTxt() (вызывается из AddJoinWebPlatform) /robots.txt первым middleware конвейера (до логирования). Настройки — секция RobotsTxt, см. ниже
Forwarded Headers ConfigureForwardedHeaders() (вызывается из AddJoinWebPlatform) Обработка X-Forwarded-Proto и X-Forwarded-For от reverse proxy первым middleware конвейера. Метрика joinrpg.http.requests.private_network (meter JoinRpg.WebInfrastructure) считает запросы с внутренних адресов — признак неправильной настройки. Теги для диагностики без логов: forwarded, client.subnet (/24 или /64), xff.count (0/1/2/3+), xfp (none/http/https/other), host.kind (ip/name/none), scheme (итоговая схема после ForwardedHeaders: http/https/other); как читать сочетания — в xml-doc PrivateNetworkRequestMetrics
HTTPS вызывается из AddJoinWebPlatform Редирект http → https (307, вне Development на порт 443) сразу после Forwarded Headers, кроме /health* и /metrics; HSTS вне Development (пока max-age 1 день). Рубильник HttpsRedirection:Enabled (по умолчанию true; HttpsRedirection__Enabled=false в окружении отключает редирект и HSTS)
DbContext AddJoinEfCoreDbContext<T>() PostgreSQL + EntityFramework.Exceptions + health check для каждого контекста
Миграции RegisterMigrator<T>() Регистрация DbContext + IMigratorService для применения миграций
Логин через id.joinrpg.ru AddJoinRpgAuthentication<T>(), MapAuthEndpoints(), IJoinUserLoginHandler OpenIddict client (authorization code flow), cookie-схема, эндпойнты /login, /signin-joinrpg, /logout
Конфигурация хостов JoinRpgHostNamesOptions Имена хостов всех сервисов JoinRpg (основной сайт, IdPortal, KogdaIgra, рейтинг)
MVC фильтры SerilogMvcFilter, SerilogRazorPagesFilter Обогащение логов данными из HTTP-контекста для MVC и Razor Pages

Требования по конфигурации

appsettings.json

{
  "ConnectionStrings": {
    "DataProtection": "Host=localhost;Port=5432;Database=joinrpg;Username=joinrpg;Password=..."
  },
  "Logging": {
    "Structured": false,
    "SelfLogEnabled": false,
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "JoinRpgHostNames": {
    "MainHost": "joinrpg.ru",
    "IdHost": "id.joinrpg.ru",
    "KogdaIgraHost": "kogda-igra.ru",
    "RatingHost": "rating.bastilia.ru"
  }
}

Описание параметров

ConnectionStrings
Ключ Обязателен Описание
DataProtection Да, кроме AddJoinWebPlatform<TKeysContext> Строка подключения PostgreSQL для хранения ключей Data Protection
Любой другой По необходимости Строки подключения для каждого DbContext, зарегистрированного через AddJoinEfCoreDbContext
Logging
Параметр Тип По умолчанию Описание
Structured bool false true — JSON-формат логов (для production/Elasticsearch), false — текстовый формат (для разработки)
SelfLogEnabled bool false Включить вывод внутренних ошибок Serilog в stderr
LogLevel Dictionary {} Уровни логирования по категориям (аналог стандартного Logging:LogLevel)
JoinRpgHostNames
Параметр Описание
MainHost Основной сайт JoinRpg (например, joinrpg.ru)
IdHost Портал аккаунтов (например, id.joinrpg.ru)
KogdaIgraHost Сервис KogdaIgra
RatingHost Сервис рейтинга Бастилии
RobotsTxt

Без настроек индексирование разрешено на всех хостах, кроме служебных /health и /metrics. Остальные запреты только явные.

Параметр По умолчанию Описание
MainHost null Главный хост приложения. Если указан без порта, порт запроса не учитывается; пустая строка — то же, что null. Если задан — индексирование разрешено только на нём, остальные хосты (служебные домены и т.п.) получают Disallow: /
DisallowAll false Запретить индексирование целиком (например, для dev-окружения)
DisallowedPaths [] Пути приложения, закрытые от индексирования и на разрешённом хосте. Свои /health и /metrics инфраструктура закрывает сама

Главный хост обычно берут из JoinRpgHostNamesOptions — какой из хостов «свой», знает только приложение:

services.AddOptions<RobotsTxtOptions>()
    .Configure<IOptions<JoinRpgHostNamesOptions>>((robots, hostNames) =>
    {
        robots.MainHost ??= hostNames.Value.MainHost;
        robots.DisallowedPaths.AddRange(["/x-api"]);
    });
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2026.9.533 15 10/29/2026
2026.9.438 172 10/27/2026
2026.9.363 78 10/22/2026
2026.9.217 128 10/16/2026
2026.8.421 206 10/4/2026
2026.8.383 96 10/4/2026
2026.8.256 113 9/29/2026
2026.8.12 302 9/6/2026
2026.8.0 151 9/2/2026
2026.7.34 108 8/26/2026
2026.7.32 221 8/26/2026
2026.7.14 139 8/24/2026
2026.7.10 105 8/23/2026
2026.5.26 211 8/4/2026
2026.3.87 182 5/12/2026
2026.3.77 121 5/11/2026
2026.3.76 112 5/11/2026
2026.3.72 93 5/8/2026
2026.3.71 91 5/8/2026
2026.3.67 91 5/7/2026