Gültiger ASP.NET Core

Gültiger ASP.NET Core


Speziell für Buchliebhaber aus der Reihe "C ++ in 24 Stunden" habe ich beschlossen, einen Artikel über ASP.NET Core zu schreiben.


Wenn Sie noch nie unter .NET oder einer ähnlichen Plattform entwickelt haben, macht es keinen Sinn, für Sie unter den Schnitt zu gehen. Wenn Sie jedoch wissen möchten, was IoC, DI, DIP, Interseptoren, Middleware und Filter sind (dh alles, was sich von Core von klassischem .NET unterscheidet), müssen Sie während der Entwicklung unbedingt auf "Lesen Sie mehr" klicken Ohne all dies zu verstehen, ist es eindeutig nicht korrekt.


IoC, DI, DIP


Wenn ein Theater mit einem Kleiderbügel beginnt, beginnt ASP.NET Core mit einer Abhängigkeitsinjektion. Um mit DI umgehen zu können, müssen Sie verstehen, was IoC ist.


Wenn man über IoC spricht, erinnert man sich oft an das Hollywood-Prinzip: "Rufen Sie uns nicht an, wir rufen Sie an." Was bedeutet "Sie müssen uns nicht anrufen, wir rufen Sie selbst an."


Unterschiedliche Quellen geben unterschiedliche Muster an, auf die IoC angewendet werden kann. Und höchstwahrscheinlich geht es ihnen gut und sie ergänzen sich einfach. Hier sind einige dieser Muster: Fabrik, Service Locator, Vorlagenmethode, Beobachter, Strategie.


Schauen wir uns IoC am Beispiel einer einfachen Konsolenanwendung an.


Angenommen, wir haben zwei einfache Klassen, die eine Schnittstelle mit einer Methode implementieren:


class ConsoleLayer : ILayer { public void Write(string text) { Console.WriteLine(text); } } class DebugLayer : ILayer { public void Write(string text) { Debug.WriteLine(text); } } interface ILayer { void Write(string text); } 

Beide hängen von der Abstraktion ab (in diesem Fall fungiert die Schnittstelle als Abstraktion).


Angenommen, wir haben ein übergeordnetes Objekt, das diese Klassen verwendet:


  class Logging : ILayer { private ILayer _instance; public Logging(int i) { if (i == 1) { _instance = new ConsoleLayer(); } else { _instance = new DebugLayer(); } } public void Write(string text) { _instance.Write(text); } } 

Abhängig vom Konstruktorparameter wird die Variable _instance von einer bestimmten Klasse initialisiert. Wenn Sie Write aufrufen, wird die Ausgabe an die Konsole oder an Debug abgeschlossen. Alles scheint ziemlich gut zu sein und entspricht sogar dem ersten Teil des Prinzips der Abhängigkeitsinversion


Übergeordnete Objekte sind unabhängig von untergeordneten Objekten. Sowohl diese als auch diese hängen von Abstraktionen ab.

In unserem Fall fungiert ILayer als Abstraktion.


Wir müssen aber auch ein Objekt von noch höherem Niveau haben. Eine, die die Protokollierungsklasse verwendet


  static void Main(string[] args) { var log = new Logging(1); log.Write("Hello!"); Console.Read(); } 

Durch Initialisieren der Protokollierung mit 1 erhalten wir in der Protokollierungsklasse eine Instanz der Klasse, die Daten an die Konsole ausgibt. Wenn wir die Protokollierung mit einer anderen Nummer initialisieren, gibt log.Write Daten an Debug aus. Alles scheint zu funktionieren, aber es funktioniert schlecht. Unser übergeordnetes Objekt Main hängt von den Details des Codes des untergeordneten Objekts ab - der Protokollierungsklasse. Wenn wir etwas in dieser Klasse ändern, müssen wir den Code der Hauptklasse ändern. Um dies zu verhindern, werden wir eine Umkehrung der Kontrolle vornehmen - Umkehrung der Kontrolle. Lassen Sie die Hauptklasse steuern, was in der Protokollierungsklasse geschieht. Die Logging-Klasse erhält als Konstruktorparameter eine Instanz einer Klasse, die die ILayer-Schnittstelle implementiert


  class Logging { private ILayer _instance; public Logging(ILayer instance) { _instance = instance; } public void Write(string text) { _instance.Write(text); } } 

Und jetzt wird unsere Hauptklasse so aussehen:


  static void Main(string[] args) { var log = new Logging(new DebugLayer()); log.Write("Hello!"); Console.Read(); } 

Tatsächlich dekorieren wir unser Protokollierungsobjekt mit dem für uns erforderlichen Objekt.


Jetzt entspricht unsere Anwendung dem zweiten Teil des Abhängigkeitsinversionsprinzips:


Abstraktionen sind unabhängig von den Details. Details hängen von Abstraktionen ab. Das heißt, Wir kennen die Details der Vorgänge in der Protokollierungsklasse nicht. Wir übergeben dort nur die Klasse, die die erforderliche Abstraktion implementiert.

Es gibt einen solchen Begriff enge Kupplung - enge Verbindung. Je schwächer die Verbindung zwischen den Komponenten in der Anwendung ist, desto besser. Ich möchte darauf hinweisen, dass dieses Beispiel einer einfachen Anwendung das Ideal nicht ein wenig erreicht. Warum? Ja, da wir in der Klasse der höchsten Ebene in Main zweimal die Erstellung von Klasseninstanzen mit new verwenden. Und es gibt eine solche mnemonische Phrase „Neu ist ein Hinweis“ - das heißt, je weniger Sie Neu verwenden, desto weniger enge Verbindungen der Komponenten in der Anwendung und desto besser. Idealerweise sollten wir keinen neuen DebugLayer verwenden, sondern DebugLayer auf andere Weise erhalten. Welches? Zum Beispiel aus einem IoC-Container oder mithilfe der Reflexion eines an Main übergebenen Parameters.


Jetzt haben wir herausgefunden, was Inversion of Control (IoC) ist und was Dependency Inversion (DIP) ist. Es bleibt zu verstehen, was Dependency Injection (DI) ist. IoC ist ein Design-Paradigma. Die Abhängigkeitsinjektion ist ein Muster. Dies ist, was wir jetzt im Konstruktor der Logging-Klasse haben. Wir erhalten eine Instanz einer bestimmten Abhängigkeit. Die Protokollierungsklasse hängt von einer Instanz einer Klasse ab, die ILayer implementiert. Und diese Instanz wird durch den Konstruktor injiziert.


IoC-Container


Ein IoC-Container ist ein solches Objekt, das viele spezifische Abhängigkeiten (Abhängigkeiten) enthält. Abhängigkeit kann auch als Service bezeichnet werden - in der Regel handelt es sich um eine Klasse mit einer bestimmten Funktionalität. Bei Bedarf kann die Abhängigkeit des gewünschten Typs aus dem Behälter entnommen werden. Das Injizieren von Abhängigkeiten in einen Container ist Inject. Extrahieren - auflösen. Hier ist ein Beispiel für den einfachsten selbstgeschriebenen IoC-Container:


  public static class IoCContainer { private static readonly Dictionary<Type, Type> _registeredObjects = new Dictionary<Type, Type>(); public static dynamic Resolve<TKey>() { return Activator.CreateInstance(_registeredObjects[typeof(TKey)]); } public static void Register<TKey, TConcrete>() where TConcrete : TKey { _registeredObjects[typeof(TKey)] = typeof(TConcrete); } } 

Nur ein Dutzend Codezeilen, aber Sie können sie bereits verwenden (natürlich nicht für die Produktion, sondern für Bildungszwecke).


Sie können die Abhängigkeit (z. B. ConsoleLayer oder DebugLayer, die wir im vorherigen Beispiel verwendet haben) folgendermaßen registrieren:


  IoCContainer.Register<ILayer, ConsoleLayer>(); 

Und extrahieren Sie es wie folgt aus dem Container an der erforderlichen Stelle des Programms:


  ILayer layer = IoCContainer.Resolve<ILayer>(); layer.Write("Hello from IoC!"); 

In realen Containern ist auch Dispose () implementiert, mit dem Sie unnötige Ressourcen zerstören können.


Übrigens vermittelt der Name IoC-Container nicht genau die Bedeutung, da der Begriff IoC in der Anwendung viel weiter gefasst ist. Daher wurde in letzter Zeit der Begriff DI-Container immer häufiger verwendet (da die Abhängigkeitsinjektion immer noch angewendet wird).


Lebensdauer + verschiedene Erweiterungsmethoden in Composition Root


ASP.NET Core-Anwendungen enthalten die Datei Startup.cs, die den Ausgangspunkt der Anwendung zum Konfigurieren von DI darstellt. Konfiguriert DI in der ConfigureServices-Methode.


  public void ConfigureServices(IServiceCollection services) { services.AddScoped<ISomeRepository, SomeRepository>(); } 

Dieser Code fügt dem DI-Container die SomeRepository-Klasse hinzu, die die ISomeRepository-Schnittstelle implementiert. Die Tatsache, dass der Service mithilfe von AddScoped zum Container hinzugefügt wird, bedeutet, dass bei jeder Anforderung einer Seite eine Instanz der Klasse erstellt wird.
Sie können einem Container einen Dienst hinzufügen, ohne eine Schnittstelle anzugeben.


  services.AddScoped<SomeRepository>(); 

Diese Methode wird jedoch nicht empfohlen, da Ihre Anwendung ihre Flexibilität verliert und enge Verbindungen angezeigt werden. Es wird empfohlen, immer eine Schnittstelle anzugeben, da Sie in diesem Fall jederzeit eine Implementierung der Schnittstelle durch eine andere ersetzen können. Und wenn die Implementierungen das Liskov-Substitutionsprinzip unterstützen, ändern Sie durch Ändern des Namens der Implementierungsklasse mit einem Handgriff die Funktionalität der gesamten Anwendung.


Es gibt zwei weitere Optionen zum Hinzufügen eines Dienstes - AddSingleton und AddTransient.
Bei Verwendung von AddSingleton wird der Dienst einmal erstellt, und bei Verwendung der Anwendung wird der Aufruf an dieselbe Instanz gesendet. Verwenden Sie diese Methode besonders vorsichtig, da Speicherlecks und Multithreading-Probleme möglich sind.


AddSingleton hat eine kleine Funktion. Es kann entweder beim ersten Zugriff darauf initialisiert werden


  services.AddSingleton<IYourService, YourService>(); 

entweder sofort beim Hinzufügen zum Konstruktor


  services.AddSingleton<IYourService>(new YourService(param)); 

Auf die zweite Weise können Sie dem Konstruktor sogar einen Parameter hinzufügen.
Wenn Sie dem Konstruktor eines Dienstes, der nicht nur mit AddSingleton, sondern auch mit AddTransient / AddScoped hinzugefügt wurde, einen Parameter hinzufügen möchten, können Sie den Lambda-Ausdruck verwenden:


  services.AddTransient<IYourService>(o => new YourService(param)); 

Wenn Sie AddTransient verwenden, wird bei jedem Zugriff ein Dienst erstellt. Ideal für leichte Dienste, die keinen Speicher und keine Ressourcen verbrauchen.


Wenn mit AddSingleton und AddScoped alles mehr oder weniger klar sein soll, muss AddTransient geklärt werden. Die offizielle Dokumentation enthält ein Beispiel, in dem ein bestimmter Dienst dem DI-Container sowohl als Parameter des Konstruktors eines anderen Dienstes als auch separat unabhängig hinzugefügt wird. Und falls es separat mit AddTransient hinzugefügt wird, erstellt es seine Instanz zweimal. Ich werde ein sehr, sehr vereinfachtes Beispiel geben. Im wirklichen Leben wird es nicht zur Verwendung empfohlen, weil Klassen erben der Einfachheit halber keine Schnittstellen. Nehmen wir an, wir haben eine einfache Klasse:


  public class Operation { public Guid OperationId { get; private set; } public Operation() { OperationId = Guid.NewGuid(); } } 

Und es gibt eine zweite Klasse, die die erste als abhängigen Dienst enthält und diese Abhängigkeit als Konstruktorparameter empfängt:


  public class OperationService { public Operation Operation { get; } public OperationService (Operation operation) { Operation = operation; } } 

Jetzt injizieren wir zwei Dienste:


  services.AddTransient<Operation>(); services.AddScoped<OperationService>(); 

Fügen Sie in einigen Controllern in Aktion den Empfang unserer Abhängigkeiten hinzu und zeigen Sie die Werte im Debug-Fenster an.


  public IActionResult Index([FromServices] Operation operation, [FromServices] OperationService operationService) { Debug.WriteLine(operation.OperationId); Debug.WriteLine(operationService.Operation.OperationId); return View(); } 

Als Ergebnis erhalten wir 2 verschiedene Guid-Werte. Wenn wir jedoch AddTransient durch AddScoped ersetzen, erhalten wir als Ergebnis zwei identische Werte.


Der IoC-Container der ASP.NET Core-Anwendung enthält standardmäßig einige Dienste. Beispielsweise ist IConfiguration ein Dienst, mit dem Sie Anwendungseinstellungen aus den Dateien appsettings.json und appsettings.Development.json abrufen können. IHostingEnvironment und ILoggerFactory, mit denen Sie die aktuelle Konfiguration und eine Hilfsklasse abrufen können, die die Protokollierung ermöglicht.


Klassen werden mit der folgenden typischen Konstruktion aus dem Container abgerufen (das häufigste Beispiel):


  private readonly IConfiguration _configuration; public SomePageController(IConfiguration configuration) { _configuration = configuration; } public async Task<IActionResult> Index() { string connectionString = _configuration["connectionString"]; } 

Im Bereich des Controllers wird eine Variable mit privaten schreibgeschützten Zugriffsmodifikatoren erstellt. Die Abhängigkeit wird aus dem Container im Konstruktor der Klasse abgerufen und einer privaten Variablen zugewiesen. Ferner kann diese Variable in beliebigen Methoden oder Aktionscontrollern verwendet werden.
Manchmal möchten Sie keine Variable erstellen, um sie in nur einer Aktion zu verwenden. Dann können Sie das Attribut [FromServices] verwenden. Ein Beispiel:


  public IActionResult About([FromServices] IDateTime dateTime) { ViewData["Message"] = «  " + dateTime.Now; return View(); } 

Es sieht seltsam aus, aber um die Methode der statischen Klasse DateTime.Now () im Code nicht aufzurufen, wird manchmal so vorgegangen, dass der Zeitwert vom Dienst als Parameter abgerufen wird. Somit ist es möglich, jederzeit einen Parameter zu übergeben, was das Schreiben von Tests und in der Regel das Vornehmen von Änderungen an der Anwendung erleichtert.
Das heißt nicht, dass Statik böse ist. Statische Methoden sind schneller. Und höchstwahrscheinlich kann statisch irgendwo im IoC-Container selbst verwendet werden. Wenn wir unsere Anwendung jedoch vor allem Statischen und Neuen schützen, erhalten wir mehr Flexibilität.


DI-Container von Drittanbietern


Was wir uns angesehen haben und was der ASP.NET Core DI-Container standardmäßig tatsächlich implementiert, ist die Konstruktorinjektion. Es besteht weiterhin die Möglichkeit, Abhängigkeiten mithilfe der sogenannten Eigenschaftsinjektion in Eigenschaften einzufügen. Diese Funktion ist jedoch in dem in ASP.NET Core integrierten Container nicht verfügbar. Beispielsweise haben wir möglicherweise eine Klasse, die Sie als Abhängigkeit implementieren, und diese Klasse verfügt über eine Art öffentliches Eigentum. Stellen Sie sich nun vor, dass wir während oder nach dem Einfügen der Abhängigkeit den Wert der Eigenschaft festlegen müssen. Kehren wir zu einem Beispiel zurück, das dem kürzlich untersuchten Beispiel ähnelt.
Wenn wir eine solche Klasse haben:


  public class Operation { public Guid OperationId { get; set; } public Operation() {} } 

was wir als Sucht einführen können,


  services.AddTransient<Operation>(); 

Dann können wir mit dem Standardcontainer den Wert für die Eigenschaft nicht festlegen.
Wenn Sie diese Gelegenheit nutzen möchten, um einen Wert für die OperationId-Eigenschaft festzulegen, können Sie einen DI-Container eines Drittanbieters verwenden, der die Eigenschaftsinjektion unterstützt. Übrigens wird die Eigenschaftsinjektion nicht besonders empfohlen. Es gibt jedoch immer noch Method Injection und Setter Method Injection, die für Sie nützlich sein können und die auch vom Standardcontainer nicht unterstützt werden.


Container von Drittanbietern können andere sehr nützliche Funktionen aufweisen. Wenn Sie beispielsweise einen Container eines Drittanbieters verwenden, können Sie Controller nur mit Abhängigkeiten versehen, deren Name ein bestimmtes Wort enthält. Und häufig verwendete Fall - DI-Container, optimiert für die Leistung.
Hier ist eine Liste einiger DI-Container von Drittanbietern, die von ASP.NET Core unterstützt werden: Autofac, Castle Windsor, LightInject, DryIoC, StructureMap, Unity


Obwohl Sie einen Standard-DI-Container verwenden, können Sie die Eigenschafts- / Methodeninjektion nicht verwenden, aber Sie können einen abhängigen Dienst als Konstruktorparameter implementieren, indem Sie das Factory-Muster wie folgt implementieren:


  services.AddTransient<IDataService, DataService>((dsvc) => { IOtherService svc = dsvc.GetService<IOtherService>(); return new DataService(svc); }); 

In diesem Fall gibt GetService null zurück, wenn der abhängige Dienst nicht gefunden wird. Es gibt eine Variante von GetRequiredService, die eine Ausnahme auslöst, wenn der abhängige Dienst nicht gefunden wird.
Der Prozess zum Abrufen eines abhängigen Dienstes mithilfe von GetService wendet tatsächlich das Dienstlokalisierungsmuster an.


Autofac


Werfen wir einen Blick auf Autofac mit einem praktischen Beispiel. Bequemerweise können Dienste aus dem Container sowohl standardmäßig als auch über Autofac registriert und empfangen werden.


Installieren Sie das NuGet-Paket Autofac.Extensions.DependencyInjection.
Ändern Sie den von der ConfigureServices-Methode zurückgegebenen Wert von void in IServiceProvider. Und Eigentum hinzufügen


  public IContainer ApplicationContainer { get; private set; } 

Danach kann am Ende der ConfigureServices-Methode der Startup-Klasse Code wie der folgende hinzugefügt werden (dies ist nur eine der Optionen zum Registrieren von Diensten):


  services.AddTransient<ISomeRepository, SomeRepository>(); var builder = new ContainerBuilder(); builder.Populate(services); builder.RegisterType<AnotherRepository>().As<IAnotherRepository>(); this.ApplicationContainer = builder.Build(); return new AutofacServiceProvider(this.ApplicationContainer); 

Hier builder.Populate (Dienste); Fügt dem Container Dienste aus der IServiceCollection hinzu. Nun und weiter ist es bereits möglich, Dienste bei builder.RegisterType zu registrieren. Oh ja. Ich hätte es fast vergessen. Sie müssen den Rückgabewert der ConfigureServices-Methode von void in IServiceProvider ändern.


AOP mit ASP.NET Core - Autofac Interseptors


Wenn sie über aspektorientierte Programmierung sprechen, erwähnen sie einen anderen Begriff - Querschnittsthemen. Bedenken sind einige Informationen, die sich auf den Code auswirken. In der russischen Version verwenden sie das Wort Verantwortung. Querschnittsthemen sind Verantwortlichkeiten, die sich auf andere Verantwortlichkeiten auswirken. Aber im Idealfall sollten sie sich nicht gegenseitig beeinflussen, oder? Wenn sie sich gegenseitig beeinflussen, wird es schwieriger, das Programm zu ändern. Es ist bequemer, wenn wir alle Operationen separat haben. Protokollierung, Transaktionen, Caching und vieles mehr können mit AOP durchgeführt werden, ohne den Code der Klassen und Methoden selbst zu ändern.


In der .NET-Welt wird häufig eine Methode verwendet, wenn AOP-Code mithilfe eines Postprozessors in einen bereits kompilierten Anwendungscode ( PostSharp ) eingebettet wird . Alternativ können Sie Interceptors verwenden. Dies sind Ereignis-Hooks, die dem Anwendungscode hinzugefügt werden können. Diese Abfangjäger verwenden in der Regel den Dekorator, den wir bereits für ihre Arbeit untersucht haben.


Lassen Sie uns Ihren eigenen Abfangjäger erstellen. Das einfachste und typischste Beispiel, das am einfachsten zu reproduzieren ist, ist die Protokollierung.
Zusätzlich zum Paket Autofac.Extensions.DependencyInjection installieren wir auch das Paket Autofac.Extras.DynamicProxy
Installiert? Fügen Sie eine einfache Protokollklasse hinzu, die beim Zugriff auf bestimmte Dienste aufgerufen wird.


  public class Logger : IInterceptor { public void Intercept(IInvocation invocation) { Debug.WriteLine($"Calling {invocation.Method.Name} from Proxy"); invocation.Proceed(); } } 

Zu unserer Registrierung hinzufügen Autofac-Registrierung des Abfangjägers:


  builder.Register(i => new Logger()); builder.RegisterType<SomeRepository >() .As<ISomeRepository >() .EnableInterfaceInterceptors() .InterceptedBy(typeof(Logger)); 

Und jetzt wird bei jedem Aufruf der Klasse die Intercept-Methode der Logger-Klasse aufgerufen.
So können wir unser Leben vereinfachen und nicht zu Beginn jeder Methode einen Protokolleintrag schreiben. Wir werden es automatisch haben. Auf Wunsch können wir es für die gesamte Anwendung leicht ändern oder deaktivieren.


Wir können auch .InterceptedBy (typeof (Logger)) entfernen; und fügen Sie das Abfangen von Aufrufen nur für bestimmte Anwendungsdienste mit dem Attribut [Intercept (typeof (Logger))] hinzu - Sie müssen es vor dem Klassenheader angeben.


Middleware


ASP.NET verfügt über eine bestimmte Kette von Codeaufrufen, die bei jeder Anforderung auftreten. Noch bevor die UI / MVC geladen wird, werden bestimmte Aktionen ausgeführt.


Das heißt, wenn wir zum Beispiel am Anfang der Configure-Methode der Startup.cs-Klasse den Code hinzufügen


  app.Use(async (context, next) => { Debug.WriteLine(context.Request.Path); await next.Invoke(); }); 

Dann können wir in der Debug-Konsole sehen, welche Dateien unsere Anwendungsanforderungen haben. Tatsächlich erhalten wir die Funktionen von AOP "out of box"
Ein wenig nutzloses, aber klares und informatives Beispiel für die Verwendung von Middleware, das ich Ihnen jetzt zeigen werde:


  public void Configure(IApplicationBuilder app) { app.Use(async (context, next) => { await context.Response.WriteAsync("Hello!" + Environment.NewLine); await next.Invoke(); }); app.Run(async context => { await context.Response.WriteAsync("Hello again."); }); } 

Mit jeder Anfrage beginnt eine Anrufkette. Nach dem Aufruf von next.invoke () wird von jeder App aus der Übergang zum nächsten Aufruf ausgeführt. Und alles endet nach App.Run funktioniert.
Sie können Code nur ausführen, wenn Sie auf eine bestimmte Route zugreifen.
Sie können dies mit app.Map tun:


  private static void Goodbye(IApplicationBuilder app) { app.Run(async context => { await context.Response.WriteAsync("Goodbye!"); }); } public void Configure(IApplicationBuilder app) { app.Map("/goodbye", Goodbye); app.Run(async context => { await context.Response.WriteAsync("Hello!"); }); } 

Wenn Sie jetzt nur zur Seite der Site gehen, sehen Sie den Text „Hallo!“. Wenn Sie der Adressleiste / Goodbye hinzufügen, wird Goodbye angezeigt.


Zusätzlich zu Use and Map können Sie UseWhen oder MapWhen verwenden, um der Middleware-Kette nur unter bestimmten Bedingungen Code hinzuzufügen.


Bisher gab es noch nutzlose Beispiele, oder? Hier ist ein normales Beispiel:


  app.Use(async (context, next) => { context.Response.Headers.Add("X-Frame-Options", "DENY"); context.Response.Headers.Add("X-Content-Type-Options", "nosniff"); context.Response.Headers.Add("X-Xss-Protection", "1"); await next(); }); 

Hier fügen wir jeder Anfrage Header hinzu, um die Seite vor Hackerangriffen zu schützen.


Oder hier ein Beispiel für die Lokalisierung:


  var supportedCultures = new[] { new CultureInfo("ru"), new CultureInfo("fr") }; app.UseRequestLocalization(new RequestLocalizationOptions { DefaultRequestCulture = new RequestCulture("ru"), SupportedCultures = supportedCultures, SupportedUICultures = supportedCultures }); 

Wenn Sie nun den Parameter? Culture = fr zur Seitenadresse hinzufügen, können Sie die Anwendungssprache auf Französisch umstellen (wenn Ihrer Anwendung eine Lokalisierung hinzugefügt wird, funktioniert alles).


Filter


Wenn sich die Middleware-Kette auf Prozesse vor MVC bezieht, arbeiten Filter mit MVC zusammen.
Das folgende schematische Diagramm zeigt, wie Filter funktionieren.


Filter


Zunächst werden Berechtigungsfilter ausgearbeitet. Das heißt, Sie können eine Art Filter oder mehrere Filter erstellen und in diese eine Art Autorisierungscode eingeben, der bei Anforderungen funktioniert.


Dann erfüllen sie Ressourcenfilter. Mit diesen Filtern können Sie beispielsweise einige Informationen aus dem Cache zurückgeben.


Dann erfolgt eine Datenbindung und Aktionsfilter werden ausgeführt. Mit ihrer Hilfe können Sie die an Action übergebenen Parameter und das zurückgegebene Ergebnis bearbeiten.


Mit Ausnahmefiltern, wie der Name andeutet, können Sie eine allgemeine Fehlerbehandlung für die Anwendung hinzufügen. Es sollte ziemlich praktisch sein, Fehler überall gleich zu behandeln. Eine Art AOP-shny Plus.


Mit Ergebnisfiltern können Sie vor oder nach der Ausführung des Aktionscontrollers eine Aktion ausführen. Sie sind den Aktionsfiltern sehr ähnlich, werden jedoch nur ausgeführt, wenn keine Fehler vorliegen. Geeignet für Logik, die an Ansicht gebunden ist.


. :


  public class YourCustomFilter : Attribute, IAuthorizationFilter { public async void OnAuthorization(AuthorizationFilterContext context) { // -    ,     ,    context.Result = new ContentResult() { Content = "        " }; } } 

DI ( Startup.cs)


  services.AddScoped<YourCustomFilter>(); 

- Action


  [ServiceFilter(typeof(YourCustomFilter))] 

– middleware - action . Configure


  public class MyMiddlewareFilter { public void Configure(IApplicationBuilder applicationBuilder) { applicationBuilder.Use(async (context, next) => { Debug.WriteLine("  middleware!"); await next.Invoke(); }); } } 

Action-


  [MiddlewareFilter(typeof(MyMiddlewareFilter))] 

Source: https://habr.com/ru/post/de437002/


All Articles