Kros.AspNetCore je univerzálna knižnica obsahujúca nástroje na zjednodušenie práce na Asp.Net Core web api projektoch.
- Kros.AspNetCore
Menný priestor Kros.AspNetCore.Exceptions
obsahuje výnimky, ktoré reprezetentujú Http chybové stavy.
Napríklad: ak v nejakej triede (typicky servisné triedy) potrebujete propagovať informáciu o chýbajúcom zdroji na ktorý sa dotazujeme, môžete vyvolať výnimku
NotFoundException
.
Menný priestor Kros.AspNetCore.Middlewares
obsahuje užitočné middlewares, ktoré je možné pridať do pipeliny a riešia štandardné veci.
Napríklad: zavolaním
app.UseErrorHandling()
pridáme middlewareErrorHandlingMiddleware
, ktorý spracuje výnimky zKros.AspNetCore.Exception
a pretransformuje ich na prisluchajúci Http status kód.
V adresári Extensions
sa nachádzajú rôzne rozšírenia štandardných tried (rozhraní) v Asp.Net Core, ktoré zjednodušujú prácu s nimi.
Na jednoduchú konfiguráciu slúži extension metóda ConfigureOptions<TOptions>(Configuration)
. Táto metóda triedu TOptions
napojí na nastavenia (konfigurácia napríklad v appsettings.json
) podľa názvu triedy a triedu zaregistruje do DI kontajnera ako:
IOptions<TOptions>
,IOptionsSnapshot<TOptions>
(štandardná registrácia metódouConfigure
)TOptions
Keďže je trieda zaregistrovaná aj priamo, v iných triedach je možné ju priamo používať ako závislosť (tzn. nie je potrebné
používať komplikovanejšiu závislosť na IOptions<TOptions>
). Trieda je registrovaná ako Transient
a podporuje hot reload.
Takže ak sa zmenia nastavenia, ďalšia inštancia TOptions
vypýtaná z DI kontajnera bude mať nové nastavenia.
Nastavenia v konfigurácii sa hľadajú podľa názvu triedy, pričom ak trieda má koncovku Options
, alebo Settings
, táto
koncovka ignoruje. Tzn. trieda SmtpSettings
alebo SmtpOptions
je nastavená podľa sekcie s názvom Smtp
.
Konfiguráciu je možné validovať implementovaním rozhrania IValidatable
v triede nastavení. Rozhranie má jedinú metódu
Validate()
, ktorá vykonáva validáciu. V prípade chyby validácia vyvolá ľubovoľnú výnimku, pričom preferovaná je výnimka
SettingsValidationException
. Pre jednoduchšiu implementáciu nastavení ktoré sa validujú je vytvorená základná trieda
AnnotatedSettingsBase
, ktorá implementuje validáciu pomocou data annotations atribútov. Takže samotnú validáciu nie je
potrebné implementovať, akurát príslušnými atribútmi odekorovať potrebné vlastnosti. (Ak validácia zlyhá, nie je vyvolaná
výnimka SettingsValidationException
, ale ValidationException
z data annotácií.)
Aby sa samotná validácia spustila, je potrebné v ConfigureServices
zaregistrovať validačný IStartupFilter
volaním
extension metódy UseConfigurationValidation()
. Validácia prebehne jednorázovo po spustení aplikácie, ale ešte pred tým,
než sú obsluhované požiadavky. Pomocou validácie je možné vynútiť základné nutné nastavenia, bez ktorých nemá spustenie
aplikácie zmysel.
public class SmtpSettings : AnnotatedSettingsBase
{
// This value is required, so it must be set in configuration file.
[Required]
public string Server { get; set; }
public int Port { get; set; }
}
public class Startup
{
public virtual void ConfigureServices(IServiceCollection services)
{
// Adds SmtpSettings class into DI container and loads the settings from "Smtp" section in configuraion.
services.ConfigureOptions<SmtpSettings>(Configuration);
services.AddSingleton<SmtpEmailSender>();
// During startup, all IValidatable objects in DI container are validated and the startup fails if some
// validation fails. In this example, it will fail, if "SmtpSettings.Server" is not set in settings.
services.UseConfigurationValidation();
}
}
public class SmtpEmailSender
{
// SmtpSettings is resolved from DI container.
public SmtpEmailSender(SmtpSettings settings)
{
}
}
Na pridanie Azure App Configuration slúži metóda AddAzureAppConfiguration(HostBuilderContext)
.
Konfigurácia obsahuje hodhoty:
{
"AppConfig": {
"Endpoint": "https://example.azconfig.io",
"Settings": [ "Example" ]
}
}
Nasledujúci kód pridá konfiguračné hodnoty (ktorých kľúč má predponu "Examle") z Azure App Configuration.
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddAzureAppConfiguration(hostingContext);
});
Jednoduchšie vkladanie/získavanie komplexných typov z/do distribuovanej keše.
Taktiež umožňuje získať hodnotu z keše a keď tam nieje tak ju vytvoriť a do keše vložiť.
var toDo = await _cache.GetAndSetAsync(
"toDo:1",
() => _database.Query().FirstOrDefault(t=> t.Id == 1),
options);
Obsahuje nastavenie CORS
policy. Je možné povoliť všetky domény pomocou AddAllowAnyOriginCors
, alebo povoliť iba vymenované domény pomocou metódy AddCustomOriginsCorsPolicy
. Tieto domény je potrebné vymenovať v appsettings.json
v sekcii AllowedHosts
.
Obe metódy nastavujú cachovanie preflight requestov (OPTIONS) defaultne na 1 hodinu.
Základná Startup
trieda obsahujúca nastavenie appsettings.json
a konfiguráciu CORS
policy. V development
a test
režime sú pre CORS
povolené všetky domény.
PR #19 adds the ability to authorize internal (downstream) services behind the api gateway.
The Api gateway will use GatewayAuthorizationMiddleware
middleware to contact the authorization service to forward the Authorization Header
from the original request. Your authorization service will validate this token and create new credential token to be routed to internal services.
To add middleware, you must first register the related services services.AddGatewayJwtAuthorization()
and then register app.UseGatewayJwtAuthorization()
to the pipeline.
You can use JwtAuthorizationHelper
to generate a Jwt token.
If you need to use claims from credential token right away, adding JwtBearerClaimsMiddleware
after GatewayAuthorizationMiddleware
will add claims from cretential token to httpContext claims.
To add JwtBearerClaimsMiddleware
register it's dependencies with services.AddJwtBearerClaims
and register app.UseJwtBearerClaims
to the pipeline.
This package contains extension for mapping JSON patch operations paths from JsonPatchDocument<TModel>
class to database columns names.
IEnumerable<Foo> columns = jsonPatch.GetColumnsNames();
This extension use flattening pattern for mapping operation path to column name.
For example: path /Supplier/Name
is maped to SupplierName
.
When you need define custom mapping, you can use JsonPatchDocumentMapperConfig<TModel>
for configuration.
JsonPatchMapperConfig<Document>
.NewConfig()
.Map(src =>
{
const string address = "/Address/";
int index = src.IndexOf(address);
if (index > -1)
{
return src.Remove(index, address.Length);
}
return src;
});
var jsonPatch = new JsonPatchDocument<Document>();
jsonPatch.Replace(p => p.Supplier.Address.Country, "Slovakia");
jsonPatch.Replace(p => p.Supplier.Address.PostCode, "0101010");
var columns = jsonPatch.GetColumnsNames();
columns.Should()
.BeEquivalentTo("SupplierCountry", "SupplierPostCode");
If you don't want map a path, then return null
from mapping function.
JsonPatchMapperConfig<Document>
.NewConfig()
.Map(src =>
{
if (src.Contains("/Address/"))
{
return null;
}
return src;
});
Častokrát sa stáva, že niektorá služba potrebuje robiť dotaz na inú službu. V taktomto prípade býva niekde v settingoch konfigurácia, ktorá obsahuje adresu danej služby. Keďže už v rámci ApiGateway budeme používať Service Discovery Provider na definíciu služieb. Tak tieto konfigurácie môžme využiť aj na to aby sme nemuseli zbytočne vo všetkých službách definovať tie isté adresy.
- Definujme si služby
"Services": {
"organizations": {
"DownstreamPath": "http://localhost:9003"
},
"authorization": {
"DownstreamPath": "http://localhost:9002",
"Paths":{
"jwt": "/api/authorization/jwt-token"
}
},
"toDos": {
"DownstreamPath": "http://localhost:9001"
}
}
Ideálne však v Azure AppConfiguration, aby sme jednotlivé definície mohli zdieľať naprieš službami.
- Pridajme si
IServiceDiscoveryProvider
services.AddServiceDiscovery();
- Použime
IServiceDiscoveryProvider
injecnutý do vašich tried
provider.GetPath("authorization","jwt");
Druhá možnosť ako používať provider na získanie služby, je vytvoriť si ľuboboľný Enum na rozlíšenie služieb, a jednotlivým hodnotám enumu nastaviť atribút ServiceNameAttribute
public enum ServiceType
{
[ServiceName("authorization")]
Authorization,
[ServiceName("organizations")]
Organizations,
[ServiceName("toDos")]
ToDos
}
Potom je možné volať získavanie služieb pomocou hodnôt enumu
provider.GetPath(ServiceType.Authorization, "jwt");
alebo
provider.GetService(ServiceType.Organizations);
V prípade Docker prostredia je možné využiť situáciu, že služby bežia pod rovnakým portom a názov hostu je znodný s názvom služby. V tomto prípade je potrebné nastaviť vlastnosť AllowServiceNameAsHost
na true
.
services.AddServiceDiscovery((o) =>
{
o.AllowServiceNameAsHost = true;
});
Je možné explicitne definovať Port
a Scheme
.
services.AddServiceDiscovery((o) =>
{
o.AllowServiceNameAsHost = true;
o.Port = 443;
o.Scheme = "https";
});
Pokiaľ ich nešpecifikujeme tak sa použije
80
ahttp
.
V prípade, že chcete niektorú adresu vynútiť (nechcete sa aby sa použil názov služby ako host, ale aby sa použila zadaná DownstreamPath
, tak je potebné zadať vlastnostnosť ForceDownstreamPath:true
).
"catalog": {
"DownstreamPath": "http://localhost:9004",
"ForceDownstreamPath": true,
"paths": {
"create": "/api/catalog"
}
},
Už aj GatewayAuthorizationMiddleware
podporuje IServiceDiscoveryProvider
"GatewayJwtAuthorization": {
"Authorization": {
"ServiceName": "authorization",
"PathName": "jwt"
},
"HashAuthorization": {
"ServiceName": "authorization",
"PathName": "jwt"
},
"CacheSlidingExpirationOffset": "00:01:00",
"CacheAbsoluteExpiration": "00:04:00",
"IgnoredPathForCache": [
"/organizations"
]
}