Atis.Hosting.AspNetCore
2.0.0-preview.5
dotnet add package Atis.Hosting.AspNetCore --version 2.0.0-preview.5
NuGet\Install-Package Atis.Hosting.AspNetCore -Version 2.0.0-preview.5
<PackageReference Include="Atis.Hosting.AspNetCore" Version="2.0.0-preview.5" />
<PackageVersion Include="Atis.Hosting.AspNetCore" Version="2.0.0-preview.5" />
<PackageReference Include="Atis.Hosting.AspNetCore" />
paket add Atis.Hosting.AspNetCore --version 2.0.0-preview.5
#r "nuget: Atis.Hosting.AspNetCore, 2.0.0-preview.5"
#:package Atis.Hosting.AspNetCore@2.0.0-preview.5
#addin nuget:?package=Atis.Hosting.AspNetCore&version=2.0.0-preview.5&prerelease
#tool nuget:?package=Atis.Hosting.AspNetCore&version=2.0.0-preview.5&prerelease
Atis.Hosting.AspNetCore
ATIS host-larının (API və ya MVC WebUI) vahid qeydiyyat və boru xətti qurulması: Serilog/Graylog,
sağlamlıq (health) uc nöqtələri, CORS, Consul, korrelyasiya id-si, ProblemDetails, forwarded
headers və — yalnız API host üçün — Swagger, API versiyalama və JWT bearer.
İki giriş nöqtəsi
AddAtisApiHost / UseAtisApiHost |
AddAtisWebUiHost / UseAtisWebUiHost |
|
|---|---|---|
| Nə üçün | Yalnız Controllers/minimal API (Razor View yoxdur) |
MVC + Razor View, session-lı servislər |
| Ortaq özəyə əlavə | Swagger sənəd generasiyası, Asp.Versioning API versiyalama, (Sso bölməsi varsa) JWT bearer |
Session (AddDistributedMemoryCache + AddSession), AddControllersWithViews(), (aktivdirsə) Razor runtime compilation, (assembly verilibsə) FluentValidation |
| Autentifikasiya sxemi | Yalnız Sso bölməsi mövcuddursa JWT bearer öz-özünə qurulur |
Heç bir sxem qurulmur — aşağıya bax |
Hər ikisi ortaq özəyi (AddAtisSharedHost/UseAtisSharedHost) çağırır: CORS/health/Consul/logging
accessor-ları, ProblemDetails + exception handler, audit körpüsü, ForwardedHeaders.
AddAtisWebUiHost heç bir autentifikasiya sxemi qurmur
atis-service-user-in WebUI-si OpenIddict serveridir; digər servislər Atis.Identity.Sso paketi
(AddAtisSsoRelyingParty(cfg, policies) + app.MapAtisSso()) ilə OIDC relying party olur. Bu host yalnız
UseAuthentication() (qeydiyyatdan keçmiş sxem provayderi varsa şərti) / UseAuthorization()-u
boru xəttində düzgün yerə qoyur — sxemi (cookie + OIDC) həmin paket, ya da servisin özü qeyd edir.
ConfigureAtisSerilog ayrıca çağırılır
Serilog builder.Host (IHostBuilder) üzərində qurulur, IServiceCollection üzərində yox — ona
görə IServiceCollection extension-ı (AddAtisApiHost/AddAtisWebUiHost) ola bilməz. Mütləq
AddAtisApiHost/UseAtisApiHost (və ya WebUI ekvivalenti)-dan əvvəl çağırılmalıdır: hər iki
UseAtisApiHost()/UseAtisWebUiHost() DI-da IDiagnosticContext (Serilog.Extensions.Hosting)
tapmasa, InvalidOperationException (Azərbaycan mesajı ilə) atır. Bu qəsdəndir — unudulsa sorğu
logu və Graylog-a gedən hər şey əvvəllər səssizcə itirdi.
Sink-lər: konsol və — yalnız Graylog:Enabled=true olanda — Graylog. Fayl sink-i YOXDUR
(konteynerdə yanlış defolt olardı: log fayla yazılıb host-dan görünməz qalar, disk dolar). Logger
qurulanda ReadFrom.Configuration(...) çağırılır — yəni servis öz Serilog:WriteTo bölməsi ilə əlavə
sink qoşa bilər, ancaq həmin sink paketinə öz PackageReference-ini özü əlavə etməlidir (bu paket
yalnız konsol və Graylog sink-lərini gətirir).
Program.cs — API host
using Atis.Hosting.AspNetCore.Extensions;
var builder = WebApplication.CreateBuilder(args);
builder.ConfigureAtisSerilog(); // MƏCBURİ — AddAtisApiHost/UseAtisApiHost-dan ƏVVƏL
builder.Services.AddAtisApiHost(builder.Configuration);
builder.Services.AddControllers();
// İcazə siyasəti lazımdırsa:
// builder.Services.AddAtisAuthorization(o => o.AddPermissionPolicies(StaffPermissions.All));
// DB audit sütunlarının (CreatedUserId/UpdatedUserId) avtomatik dolması üçün:
// builder.Services.AddAtisPersistence<StaffDbContext>(builder.Configuration, o => o.Schema = "staff");
var app = builder.Build();
app.UseAtisApiHost();
// DİQQƏT: UseAtisApiHost() artıq UseRouting() çağırıb — burada YENİDƏN app.UseRouting() YAZMA,
// birbaşa endpoint mapping-ə keç.
app.MapControllers();
app.Run();
Program.cs — WebUI host
using System.Reflection;
using Atis.Hosting.AspNetCore.Extensions;
using Microsoft.AspNetCore.Authentication.Cookies;
var builder = WebApplication.CreateBuilder(args);
builder.ConfigureAtisSerilog(); // MƏCBURİ — AddAtisWebUiHost/UseAtisWebUiHost-dan ƏVVƏL
// Assembly.GetExecutingAssembly() ÖZ servisinin assembly-sidir — FluentValidation validator-ları
// ordan axtarılır. null ötürülsə, FluentValidation heç qoşulmur.
builder.Services.AddAtisWebUiHost(builder.Configuration, Assembly.GetExecutingAssembly());
// Autentifikasiya sxemini bu paket qurmur — Atis.Identity.Sso qurur (cookie + OIDC code+PKCE,
// permission policy-ləri (ATIS qaydaları), ICurrentUser). Sonra app.MapAtisSso() ilə /account/login|logout gəlir:
builder.Services.AddAtisSsoRelyingParty(builder.Configuration, o => o.AddPermissionPolicies(StaffPermissions.All));
var app = builder.Build();
app.UseAtisWebUiHost();
// DİQQƏT: UseAtisWebUiHost() artıq UseRouting() çağırıb — burada YENİDƏN app.UseRouting() YAZMA,
// birbaşa endpoint mapping-ə keç.
app.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
⚠ Deploy-dan ƏVVƏL
1. Konfiqurasiya bölmə adları dəyişir — bu, mövcud ~20 servisin deployment-ini sındırır
| Köhnə (mövcud servislər) | Yeni |
|---|---|
ConsulConfig:Id / Name / ConsulAddress / Address |
Consul:ServiceId / ServiceName / ConsulAddress / ServiceAddress |
ServiceUrls (massiv) |
Cors:AllowedOrigins (massiv) + Cors:PolicyName |
| Graylog C#-da hardcode | Graylog:Enabled / Host / Port / Facility / TransportType |
Token:Issuer / Audience / SecurityKey |
Sso:Authority / Sso:Audience (simmetrik açar yoxdur — OIDC discovery, Sso:RequireHttpsMetadata opsional, default true) |
Servis appsettings.json-unu köçürmədən bu paketə keçsə, Consul qeydiyyatı, CORS və Graylog
səssizcə söndürülmüş vəziyyətdə qalır (aşağıdakı "bölmə yoxdursa" qaydasına görə) — heç bir
build və ya start-up xətası vermir, problem yalnız işlədikdə (məs. Consul-da servis görünmür)
üzə çıxır.
2. Swagger artıq Production-da map olunmur
Mövcud servislərdə Swagger prod-da da açıq idi — bu, qəsdən dəyişdirilib. Swagger:Enabled
DI qeydiyyatına (sənəd generasiyasına) təsir edir və mühitdən asılı deyil, amma UseSwagger()/
UseSwaggerUI() middleware-ləri yalnız !env.IsProduction() olduqda boru xəttinə qoşulur — yəni
Production mühitində (ASPNETCORE_ENVIRONMENT=Production) nə /swagger/v1/swagger.json, nə də
Swagger UI əlçatandır. API sənədinə istehsalatda ehtiyac varsa, ayrıca (bu paketdən kənar) həll
tələb olunur.
Bölmələr — şərti vs həmişə
| Bölmə | Rejim | Açarlar (default) |
|---|---|---|
Cors |
Şərti — bölmə konfiqurasiyada mövcud olmasa, CORS siyasəti heç qoşulmur | PolicyName ("DefaultPolicy"), AllowedOrigins (mütləq, mütləq URI-lər), AllowAnyHeader (true), AllowAnyMethod (true), AllowCredentials (false), ExposedHeaders (null) |
Consul |
Şərti — bölmə mövcud olmasa qeydiyyat heç qoşulmur; mövcuddursa da faktiki qeydiyyat (hosted service) yalnız Consul:Enabled=true olanda işə düşür |
Enabled (false), ServiceId, ServiceName, ConsulAddress, ServiceAddress (Enabled=true olanda hamısı mütləq), HealthCheckPath ("/api/health"), HealthCheckInterval ("10s"), HealthCheckTimeout ("5s"), DeregisterCriticalServiceAfter ("30s"), Tags, Meta |
Graylog |
Həmişə bind olunur (bölmə yoxdursa defolt dəyərlərlə: söndürülmüş) | Enabled (false), Host, Port, Facility (Enabled=true olanda üçü də mütləq — boş Facility sink-in öz defoltunu əvəz edir və Graylog-da stream marşrutlamasını sındırır), TransportType ("Udp") |
HealthEndpoints |
Həmişə bind olunur — bölmə olmasa da kanonik /api/health işə düşür |
Path ("/api/health"), ReadinessPath (null), LivenessPath (null), UsePlainTextResponse (true), PlainText ("Ok"), SuppressRequestLogging (true) |
Sso |
Şərti, yalnız API host — bölmə mövcud olanda JWT bearer qoşulur və açarlar start-da yoxlanılır | Authority (mütləq, mütləq http(s) URI), Audience (mütləq, boş ola bilməz), RequireHttpsMetadata (true) |
Swagger |
Həmişə bind olunur, yalnız API host | Enabled (true), Title ("ATIS API"), Version ("v1") |
WebUi |
Həmişə bind olunur, yalnız WebUI host | SessionTimeoutMinutes (60), RazorRuntimeCompilation (true), ErrorPath ("/Home/Error") |
Bütün bölmələr ValidateOnStart() ilə qeydiyyatdan keçir, amma məzmun qaydaları hamısında yoxdur:
Cors,Consul,Graylog,HealthEndpoints,Sso— öz validator-ları var, yanlış dəyər host qalxandaOptionsValidationExceptionilə düşür (ilk sorğuda yox).Swagger,WebUi— yalnız bind olunur: tip uyuşmazlığı (məs."Enabled": "bəli") start-da düşür, amma məzmun qaydası (hansı dəyər məqbuldur) yoxlanılmır.
appsettings.json nümunəsi (dəyərlər yalnız nümunədir):
{
"Cors": { "AllowedOrigins": ["https://staff.atis.edu.az"] },
"Consul": {
"Enabled": true,
"ServiceId": "atis-staff-1",
"ServiceName": "atis-staff",
"ConsulAddress": "http://consul:8500",
"ServiceAddress": "http://atis-staff:8080"
},
"Graylog": { "Enabled": true, "Host": "graylog.internal", "Port": 12201 },
"Sso": { "Authority": "https://user.atis.edu.az", "Audience": "atis.staff.api" }
}
Boru xətti sırası
Ortaq hissə (UseAtisSharedHost, hər iki host tipində eyni, sabit sıra):
UseForwardedHeaders → UseAtisCorrelationId → UseExceptionHandler → (Cors bölməsi varsa) UseCors → health middleware
Korrelyasiya id-si exception handler-dən əvvəl gəlməlidir ki, xəta cavabı korrelyasiya id-sini
daşısın. Health uc nöqtəsi burada IApplicationBuilder-in UseHealthChecks(...) middleware
overload-u ilə qoşulur (endpoint routing yox) — ona görə /api/health UseRouting()-dən əvvəl
işləyir.
API host davamı: UseSerilogRequestLogging → (prod deyilsə və Swagger:Enabled-dırsa) UseSwagger/UseSwaggerUI → UseRouting → (sxem qeydiyyatdaydırsa) UseAuthentication → UseAuthorization
WebUI host davamı: UseSerilogRequestLogging → UseStaticFiles → UseRouting → (sxem qeydiyyatdaydırsa) UseAuthentication → UseAuthorization → UseSession
Nə üçün UseRouting()-i özün çağırmamalısan
Həm UseAtisApiHost(), həm də UseAtisWebUiHost() UseRouting()-i özü çağırır — servis
app.UseRouting() YAZMAMALIDIR, birbaşa MapControllers()/MapControllerRoute(...)-ə keçməlidir.
Səbəb: AuthorizationMiddleware [Authorize] metadata-sını context.GetEndpoint()-dən oxuyur, bu
isə routing icra olunmayınca null-dur — routing-dən əvvəl autentifikasiya/icazələndirmə qoyulsa,
icazə heç vaxt yoxlanılmır.
UseSession() niyə UseAuthorization()-dan sonra gəlir
Mövcud servislərdə ISessionManager institution scoping-i session-da keşləyir və bunu
authorization-dan sonra oxuyur — sıra bunu qorumaq üçün qəsdən belədir. AddAtisWebUiHost
AddDistributedMemoryCache()-i TryAdd-based qeyd edir, ona görə servis onu AddStackExchangeRedisCache()
və s. ilə əvəz edə bilər.
AddAuthorization() niyə şərtsiz çağırılır
AddAtisSharedHost Sso/sxem mövcudluğundan asılı olmayaraq şərtsiz AddAuthorization() çağırır —
əks halda UseAuthorization() (hər iki host tipində şərtsiz çağırılır) IAuthorizationPolicyProvider
tapmadığına görə host qalxarkən InvalidOperationException atardı. Servis öz permission
siyasətlərini AddAtisAuthorization/AddAuthorization ilə əlavə etdikdə bu, sadəcə əlavə
konfiqurasiya kimi üstünə gəlir — konflikt yaratmır.
X-Correlation-ID
CorrelationIdMiddleware gələn X-Correlation-ID başlığını götürür; yoxdursa (və ya
128 simvoldan uzundursa — log-injection/başlıq şişməsi riski) yeni GUID yaradır. Dəyər
HttpContext.Items["Atis.CorrelationId"]-ə yazılır, cavab başlığına (Response.OnStarting ilə)
əlavə olunur və Serilog LogContext-inə (CorrelationId property-si kimi) sorğunun sonuna qədər
push olunur.
ProblemDetails cavabı
Tutulmamış exception RFC 7807 application/problem+json cavabına çevrilir.
Cavabda exception mesajı və stack trace OLMUR — onlar yalnız loga (ILogger) yazılır. Cavab
yalnız ümumi mətn, Status=500, traceId (HttpContext.TraceIdentifier) və (varsa)
correlationId extension-larını daşıyır.
WebUI host-da bu, Accept başlığından asılı olaraq fərqlənir: brauzer sorğuları (Accept: text/html, application/json olmadan) ProblemDetails ALMIR — WebUi:ErrorPath-a (default
"/Home/Error", portlanmış ATIS HomeController.Error) yenidən icra olunur və HTML xəta səhifəsi
qaytarılır. JSON/AJAX sorğular (Accept: application/json, ya da Accept heç göndərilməyibsə)
adi qaydada ProblemDetails alır. API host-da bu fərqləndirmə YOXDUR — AtisHtmlErrorPolicy.None
qeydiyyatdadır, ona görə hər sorğu (brauzer olsa belə) həmişə ProblemDetails alır.
Audit körpüsü
AddAtisSharedHost (deməli, AddAtisApiHost/AddAtisWebUiHost-un ikisi də) IAuditUserProvider-i
CurrentUserAuditProvider-ə (Atis.Identity.Token-in ICurrentUser-i üzərində qurulub) açıq
AddScoped ilə bağlayır — Atis.Persistence-in AddAtisPersistence-də TryAddScoped ilə qeyd
etdiyi NullAuditUserProvider-i həmişə əvəz edir (sıra əhəmiyyətsizdir, "son qeydiyyat qalib gəlir").
Servis eyni zamanda AddAtisPersistence ilə işlədəndə CreatedUserId/UpdatedUserId sütunları
avtomatik dolur — servisin özü heç nə etmir.
AddAtisSharedHost həmçinin (TryAdd-based, idempotent) AddAtisIdentity()-ni özü çağırır —
körpü konstruktorunda ICurrentUser tələb etdiyi üçün, servis yalnız AddAtisApiHost/
AddAtisWebUiHost çağırıb heç bir permission siyasəti (AddAtisAuthorization) qurmasa belə, host
özü-özünə bəs edən qalır.
ForwardedHeaders
KnownProxies/KnownNetworks təmizlənir — yəni gələn X-Forwarded-For/X-Forwarded-Proto
başlıqlarına etibar edilir. Bu, yalnız etibarlı reverse proxy (nginx/ingress/Consul-Connect və
s.) arxasında düzgündür; açıq internetə birbaşa ifşa olunan host üçün yararsızdır (client-in özü
bu başlıqları saxtalaşdıra bilər).
Swagger (API host)
Swagger:Enabled (default true) DI-da AddSwaggerGen-i qoşur — Bearer security scheme ilə
birlikdə (Swagger UI-dan "Authorize" düyməsi işləsin deyə, Sso qoşulmasa belə). XML doc faylı
yalnız servisin öz assembly-sindən ({EntryAssembly}.xml) və yalnız mövcuddursa qoşulur.
UseSwagger()/UseSwaggerUI() — yuxarıdakı ⚠ Deploy-dan ƏVVƏL bölməsinə bax.
API versiyalama
Asp.Versioning ilə: DefaultApiVersion = 1.0, AssumeDefaultVersionWhenUnspecified = true,
ReportApiVersions = true, URL-də qrup formatı 'v'VVV (SubstituteApiVersionInUrl = true).
JWT bearer (yalnız API host, Sso bölməsi mövcud olanda)
Dəyərlər xam bölmədən yox, tipli və start-da yoxlanılan SsoOptions-dan gəlir:
o.Authority = sso.Value.Authority;
o.Audience = sso.Value.Audience;
o.RequireHttpsMetadata = sso.Value.RequireHttpsMetadata;
o.MapInboundClaims = false;
o.TokenValidationParameters.RoleClaimType = "role";
o.TokenValidationParameters.NameClaimType = "sub";
MapInboundClaims = false, RoleClaimType = "role", NameClaimType = "sub" MƏCBURİDİR —
Atis.Identity.Token-in claim oxuyucuları OIDC token-inin xam "role"/"sub" claim adlarını
gözləyir. MapInboundClaims true olsa (defolt), JwtBearerHandler bu adları
ClaimTypes.Role/ClaimTypes.NameIdentifier-ə çevirər və Atis.Identity.Token-in oxuyucuları heç
nə tapmaz. Bu üç seçim məhz buna görə Atis.Identity.Token-in OIDC token-ləri ilə işləməsini təmin
edir.
FluentValidation (yalnız WebUI host)
AddAtisWebUiHost(configuration, validatorAssembly) — validatorAssembly verilməyibsə (default
null), FluentValidation heç qoşulmur. Bu paket özü heç vaxt Assembly.GetExecutingAssembly()
işlətmir (bu, paketin öz assembly-sini qaytarardı, servisinkini yox) — servis onu açıq ötürməlidir:
services.AddAtisWebUiHost(cfg, Assembly.GetExecutingAssembly()). Verilibsə,
AddFluentValidationAutoValidation() (MVC model validasiyasını avtomatlaşdırır) və
AddValidatorsFromAssembly(validatorAssembly) (servisin öz IValidator<T> implementasiyalarını
qeyd edir) cütü qoşulur — FluentValidation.AspNetCore-un [Obsolete] AddFluentValidation(...)
genişləndirməsi əvəzinə.
Session (yalnız WebUI host)
WebUi:SessionTimeoutMinutes (default 60) idle timeout təyin edir; cookie HttpOnly=true,
IsEssential=true. IDistributedCache AddDistributedMemoryCache() ilə TryAdd-based qeyd olunur
— servis onu Redis və s. ilə əvəz edə bilər.
Razor runtime compilation (yalnız WebUI host)
WebUi:RazorRuntimeCompilation (default true) aktivdirsə, AddRazorRuntimeCompilation() qoşulur
— .cshtml dəyişiklikləri dotnet build etmədən dərhal görünür.
HSTS (preview.4)
UseAtisSharedHost (deməli UseAtisApiHost/UseAtisWebUiHost) ForwardedHeaders-dən dərhal sonra, Development
xaric, UseHsts() çağırır. Konteyner TLS proxy arxasında plain HTTP-dədir — Request.IsHttps yalnız
X-Forwarded-Proto oxunandan sonra doğrudur; başlıq bütün cavablara (static fayllar, /api/health, OIDC challenge
302, 403) düşür. Servis özü UseHsts() çağırmasın — ikiqat Strict-Transport-Security başlığı yaranar
(atis-service-user WebUI-də hələ var, təmizlənməlidir).
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
-
net8.0
- Asp.Versioning.Mvc (>= 8.1.0)
- Asp.Versioning.Mvc.ApiExplorer (>= 8.1.0)
- Atis.Core (>= 2.0.0-preview.5)
- Atis.Identity.Token (>= 2.0.0-preview.5)
- Consul (>= 1.7.14.6)
- FluentValidation.AspNetCore (>= 11.3.0)
- Microsoft.AspNetCore.Authentication.JwtBearer (>= 8.0.18)
- Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation (>= 8.0.15)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 8.0.0)
- Serilog.AspNetCore (>= 8.0.3)
- Serilog.Sinks.Graylog (>= 3.1.1)
- Swashbuckle.AspNetCore (>= 6.6.2)
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 |
|---|---|---|
| 2.0.0-preview.5 | 42 | 10/1/2026 |
| 2.0.0-preview.4 | 93 | 9/14/2026 |
2.0.0-preview.1: AddAtisApiHost/AddAtisWebUiHost, ConfigureAtisSerilog, X-Correlation-ID, ProblemDetails, tipli və start-da yoxlanılan options (Consul, Cors, Graylog, HealthEndpoints, Sso, Swagger, WebUi). DİQQƏT: konfiqurasiya bölmə adları köhnə servislərdən fərqlidir — README-yə bax. preview.2: WebUi:ErrorPath + AtisHtmlErrorPolicy — WebUI host-da brauzer (Accept: text/html) sorğuları tutulmamış exception-da ProblemDetails yox, HTML xəta səhifəsinə (ErrorPath) yenidən icra olunur; API host-da davranış dəyişmir. preview.4: UseAtisSharedHost ForwardedHeaders-dən dərhal sonra (Development xaric) UseHsts() çağırır — servis özü UseHsts çağırmasın (ikiqat başlıq); proxy arxasında IsHttps düzəldikdən sonra bütün cavablar (static, health, challenge, 403) HSTS daşıyır.