Migrieren von HTTP-Handlern und Modulen zu ASP.net Core MiddlewareMigrate HTTP handlers and modules to ASP.NET Core middleware

In diesem Artikel wird gezeigt, wie vorhandene ASP.net -HTTP-Module und-Handler von System. Webserver zu ASP.net Core Middlewaremigriert werden.This article shows how to migrate existing ASP.NET HTTP modules and handlers from system.webserver to ASP.NET Core middleware.

Wieder besuchte Module und HandlerModules and handlers revisited

Bevor Sie mit der ASP.net Core Middleware fortfahren, können Sie zuerst die Funktionsweise von HTTP-Modulen und-Handlern fortsetzen:Before proceeding to ASP.NET Core middleware, let's first recap how HTTP modules and handlers work:

Module-Handler

Handler sind:Handlers are:

  • Klassen, die IHttpHandler implementierenClasses that implement IHttpHandler

  • Wird zum Verarbeiten von Anforderungen mit einem angegebenen Dateinamen oder einer Erweiterung verwendet, z . b.. ReportUsed to handle requests with a given file name or extension, such as .report

  • Konfiguriert in Web.configConfigured in Web.config

Module:Modules are:

  • Klassen, die IHttpModule implementierenClasses that implement IHttpModule

  • Wird für jede Anforderung aufgerufen.Invoked for every request

  • Kurzschluss (weitere Verarbeitung einer Anforderung wird beendet)Able to short-circuit (stop further processing of a request)

  • Der HTTP-Antwort kann hinzugefügt werden, oder es kann ein eigener erstellt werden.Able to add to the HTTP response, or create their own

  • Konfiguriert in Web.configConfigured in Web.config

Die Reihenfolge, in der Module eingehende Anforderungen verarbeiten, wird durch Folgendes bestimmt:The order in which modules process incoming requests is determined by:

  1. Der Lebenszyklus der Anwendung, bei dem es sich um eine Reihe von Ereignissen handelt, die von ASP.net ausgelöst werden: BeginRequest, AuthenticateRequestusw. Jedes Modul kann einen Handler für ein oder mehrere Ereignisse erstellen.The application life cycle, which is a series events fired by ASP.NET: BeginRequest, AuthenticateRequest, etc. Each module can create a handler for one or more events.

  2. Für dasselbe Ereignis die Reihenfolge, in der Sie in Web.configkonfiguriert sind.For the same event, the order in which they're configured in Web.config.

Zusätzlich zu Modulen können Sie Handler für die Lebenszyklus Ereignisse ihrer Global.asax.cs -Datei hinzufügen.In addition to modules, you can add handlers for the life cycle events to your Global.asax.cs file. Diese Handler werden nach den Handlern in den konfigurierten Modulen ausgeführt.These handlers run after the handlers in the configured modules.

Von Handlern und Modulen bis MiddlewareFrom handlers and modules to middleware

Middleware ist einfacher als HTTP-Module und-Handler:Middleware are simpler than HTTP modules and handlers:

  • Module, Handler, Global.asax.cs, Web.config (außer IIS-Konfiguration) und der Lebenszyklus der Anwendung sind nicht mehr vorhanden.Modules, handlers, Global.asax.cs, Web.config (except for IIS configuration) and the application life cycle are gone

  • Die Rollen von Modulen und Handlern wurden von Middleware übernommen.The roles of both modules and handlers have been taken over by middleware

  • Middleware wird nicht in Web.config sondern mithilfe von Code konfiguriert.Middleware are configured using code rather than in Web.config

  • Mithilfe der Pipeline Verzweigung können Sie Anforderungen an bestimmte Middleware senden. Dies basiert nicht nur auf der URL, sondern auch auf Anforderungs Headern, Abfrage Zeichenfolgen usw.Pipeline branching lets you send requests to specific middleware, based on not only the URL but also on request headers, query strings, etc.
  • Mithilfe der Pipeline Verzweigung können Sie Anforderungen an bestimmte Middleware senden. Dies basiert nicht nur auf der URL, sondern auch auf Anforderungs Headern, Abfrage Zeichenfolgen usw.Pipeline branching lets you send requests to specific middleware, based on not only the URL but also on request headers, query strings, etc.

Middleware ähnelt Modulen stark:Middleware are very similar to modules:

Middleware und Module werden in einer anderen Reihenfolge verarbeitet:Middleware and modules are processed in a different order:

  • Die Reihenfolge der Middleware basiert auf der Reihenfolge, in der Sie in die Anforderungs Pipeline eingefügt werden, während die Reihenfolge der Module hauptsächlich auf Anwendungslebenszyklus -Ereignissen basiert.Order of middleware is based on the order in which they're inserted into the request pipeline, while order of modules is mainly based on application life cycle events

  • Die Reihenfolge der Middleware für Antworten ist das Gegenteil bei Anforderungen, während die Reihenfolge der Module für Anforderungen und Antworten gleich ist.Order of middleware for responses is the reverse from that for requests, while order of modules is the same for requests and responses

  • Weitere Informationen finden Sie unter Erstellen einer middlewarepipeline mit iapplicationbuilderSee Create a middleware pipeline with IApplicationBuilder

Middleware

Beachten Sie, dass in der obigen Abbildung die Authentifizierungs Middleware die Anforderung kurzgeschlossen hat.Note how in the image above, the authentication middleware short-circuited the request.

Migrieren von Modulcode zu MiddlewareMigrating module code to middleware

Ein vorhandenes HTTP-Modul sieht in etwa wie folgt aus:An existing HTTP module will look similar to this:

// ASP.NET 4 module

using System;
using System.Web;

namespace MyApp.Modules
{
    public class MyModule : IHttpModule
    {
        public void Dispose()
        {
        }

        public void Init(HttpApplication application)
        {
            application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
            application.EndRequest += (new EventHandler(this.Application_EndRequest));
        }

        private void Application_BeginRequest(Object source, EventArgs e)
        {
            HttpContext context = ((HttpApplication)source).Context;

            // Do something with context near the beginning of request processing.
        }

        private void Application_EndRequest(Object source, EventArgs e)
        {
            HttpContext context = ((HttpApplication)source).Context;

            // Do something with context near the end of request processing.
        }
    }
}

Wie auf der Seite Middleware gezeigt, ist eine ASP.net Core Middleware eine-Klasse, die eine-Methode verfügbar macht, die eine Invoke HttpContext -Klasse und eine zurückgibt Task .As shown in the Middleware page, an ASP.NET Core middleware is a class that exposes an Invoke method taking an HttpContext and returning a Task. Die neue Middleware sieht wie folgt aus:Your new middleware will look like this:

// ASP.NET Core middleware

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace MyApp.Middleware
{
    public class MyMiddleware
    {
        private readonly RequestDelegate _next;

        public MyMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            // Do something with context near the beginning of request processing.

            await _next.Invoke(context);

            // Clean up.
        }
    }

    public static class MyMiddlewareExtensions
    {
        public static IApplicationBuilder UseMyMiddleware(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyMiddleware>();
        }
    }
}

Die vorangehende Middleware-Vorlage stammt aus dem Abschnitt zum Schreiben von Middleware.The preceding middleware template was taken from the section on writing middleware.

Die Hilfsklasse mymiddlewareextensions vereinfacht das Konfigurieren ihrer Middleware in der Startup Klasse.The MyMiddlewareExtensions helper class makes it easier to configure your middleware in your Startup class. Die- UseMyMiddleware Methode fügt der Anforderungs Pipeline die Middleware-Klasse hinzu.The UseMyMiddleware method adds your middleware class to the request pipeline. Die von der Middleware benötigten Dienste werden in den Konstruktor der Middleware eingefügt.Services required by the middleware get injected in the middleware's constructor.

Das Modul kann eine Anforderung beenden, z. b. wenn der Benutzer nicht autorisiert ist:Your module might terminate a request, for example if the user isn't authorized:

// ASP.NET 4 module that may terminate the request

private void Application_BeginRequest(Object source, EventArgs e)
{
    HttpContext context = ((HttpApplication)source).Context;

    // Do something with context near the beginning of request processing.

    if (TerminateRequest())
    {
        context.Response.End();
        return;
    }
}

Eine Middleware verarbeitet dies, indem nicht Invoke für die nächste Middleware in der Pipeline aufgerufen wird.A middleware handles this by not calling Invoke on the next middleware in the pipeline. Beachten Sie, dass dies die Anforderung nicht vollständig beendet, da vorherige Middlewares weiterhin aufgerufen werden, wenn die Antwort durch die Pipeline wieder zurückgegeben wird.Keep in mind that this doesn't fully terminate the request, because previous middlewares will still be invoked when the response makes its way back through the pipeline.

// ASP.NET Core middleware that may terminate the request

public async Task Invoke(HttpContext context)
{
    // Do something with context near the beginning of request processing.

    if (!TerminateRequest())
        await _next.Invoke(context);

    // Clean up.
}

Wenn Sie die Funktionalität Ihres Moduls zu ihrer neuen Middleware migrieren, stellen Sie möglicherweise fest, dass der Code nicht kompiliert wird, da die HttpContext Klasse in ASP.net Core erheblich geändert wurde.When you migrate your module's functionality to your new middleware, you may find that your code doesn't compile because the HttpContext class has significantly changed in ASP.NET Core. Spätererfahren Sie, wie Sie zum neuen ASP.net Core HttpContext migrieren.Later on, you'll see how to migrate to the new ASP.NET Core HttpContext.

Migrieren des Moduls in die Anforderungs PipelineMigrating module insertion into the request pipeline

HTTP-Module werden in der Regel mit Web.configder Anforderungs Pipeline hinzugefügt:HTTP modules are typically added to the request pipeline using Web.config:

<?xml version="1.0" encoding="utf-8"?>
<!--ASP.NET 4 web.config-->
<configuration>
  <system.webServer>
    <modules>
      <add name="MyModule" type="MyApp.Modules.MyModule"/>
    </modules>
  </system.webServer>
</configuration>

Konvertieren Sie diese, indem Sie die neue Middleware der Anforderungs Pipeline in ihrer Klasse hinzufügen Startup :Convert this by adding your new middleware to the request pipeline in your Startup class:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseMyMiddleware();

    app.UseMyMiddlewareWithParams();

    var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
    var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
    app.UseMyMiddlewareWithParams(myMiddlewareOptions);
    app.UseMyMiddlewareWithParams(myMiddlewareOptions2);

    app.UseMyTerminatingMiddleware();

    // Create branch to the MyHandlerMiddleware. 
    // All requests ending in .report will follow this branch.
    app.MapWhen(
        context => context.Request.Path.ToString().EndsWith(".report"),
        appBranch => {
            // ... optionally add more middleware to this branch
            appBranch.UseMyHandler();
        });

    app.MapWhen(
        context => context.Request.Path.ToString().EndsWith(".context"),
        appBranch => {
            appBranch.UseHttpContextDemoMiddleware();
        });

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

Die genaue Position in der Pipeline, an der Sie die neue Middleware einfügen, hängt von dem Ereignis ab, das als Modul ( BeginRequest , EndRequest usw.) behandelt wurde, und der Reihenfolge in der Liste der Module in Web.config.The exact spot in the pipeline where you insert your new middleware depends on the event that it handled as a module (BeginRequest, EndRequest, etc.) and its order in your list of modules in Web.config.

Wie bereits erwähnt, gibt es keinen Anwendungslebenszyklus in ASP.net Core und die Reihenfolge, in der die Antworten von der Middleware verarbeitet werden, unterscheidet sich von der von Modulen verwendeten Reihenfolge.As previously stated, there's no application life cycle in ASP.NET Core and the order in which responses are processed by middleware differs from the order used by modules. Dies könnte dazu führen, dass Ihre Bestell Entscheidung schwieriger wird.This could make your ordering decision more challenging.

Wenn die Reihenfolge zu einem Problem wird, können Sie das Modul in mehrere middlewarekomponenten aufteilen, die unabhängig voneinander angeordnet werden können.If ordering becomes a problem, you could split your module into multiple middleware components that can be ordered independently.

Migrieren von Handlercode zu MiddlewareMigrating handler code to middleware

Ein HTTP-Handler sieht in etwa wie folgt aus:An HTTP handler looks something like this:

// ASP.NET 4 handler

using System.Web;

namespace MyApp.HttpHandlers
{
    public class MyHandler : IHttpHandler
    {
        public bool IsReusable { get { return true; } }

        public void ProcessRequest(HttpContext context)
        {
            string response = GenerateResponse(context);

            context.Response.ContentType = GetContentType();
            context.Response.Output.Write(response);
        }

        // ...

        private string GenerateResponse(HttpContext context)
        {
            string title = context.Request.QueryString["title"];
            return string.Format("Title of the report: {0}", title);
        }

        private string GetContentType()
        {
            return "text/plain";
        }
    }
}

In Ihrem ASP.net Core Projekt würden Sie dies in eine Middleware übersetzen, die in etwa wie folgt aussieht:In your ASP.NET Core project, you would translate this to a middleware similar to this:

// ASP.NET Core middleware migrated from a handler

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;

namespace MyApp.Middleware
{
    public class MyHandlerMiddleware
    {

        // Must have constructor with this signature, otherwise exception at run time
        public MyHandlerMiddleware(RequestDelegate next)
        {
            // This is an HTTP Handler, so no need to store next
        }

        public async Task Invoke(HttpContext context)
        {
            string response = GenerateResponse(context);

            context.Response.ContentType = GetContentType();
            await context.Response.WriteAsync(response);
        }

        // ...

        private string GenerateResponse(HttpContext context)
        {
            string title = context.Request.Query["title"];
            return string.Format("Title of the report: {0}", title);
        }

        private string GetContentType()
        {
            return "text/plain";
        }
    }

    public static class MyHandlerExtensions
    {
        public static IApplicationBuilder UseMyHandler(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyHandlerMiddleware>();
        }
    }
}

Diese Middleware ähnelt der Middleware, die den Modulen entspricht.This middleware is very similar to the middleware corresponding to modules. Der einzige wirkliche Unterschied besteht darin, dass hier kein-Rückruf erfolgt _next.Invoke(context) .The only real difference is that here there's no call to _next.Invoke(context). Das ist sinnvoll, da sich der Handler am Ende der Anforderungs Pipeline befindet, sodass keine nächste Middleware aufgerufen werden kann.That makes sense, because the handler is at the end of the request pipeline, so there will be no next middleware to invoke.

Migrieren von handlereinfügung in die Anforderungs PipelineMigrating handler insertion into the request pipeline

Das Konfigurieren eines HTTP-Handlers erfolgt in Web.config und sieht in etwa wie folgt aus:Configuring an HTTP handler is done in Web.config and looks something like this:

<?xml version="1.0" encoding="utf-8"?>
<!--ASP.NET 4 web.config-->
<configuration>
  <system.webServer>
    <handlers>
      <add name="MyHandler" verb="*" path="*.report" type="MyApp.HttpHandlers.MyHandler" resourceType="Unspecified" preCondition="integratedMode"/>
    </handlers>
  </system.webServer>
</configuration>

Sie können dies konvertieren, indem Sie die neue handlermiddleware der Anforderungs Pipeline in ihrer Klasse hinzufügen Startup , ähnlich der von Modulen konvertierten Middleware.You could convert this by adding your new handler middleware to the request pipeline in your Startup class, similar to middleware converted from modules. Das Problem bei diesem Ansatz besteht darin, dass alle Anforderungen an die neue handlermiddleware gesendet werden.The problem with that approach is that it would send all requests to your new handler middleware. Es ist jedoch nur gewünscht, dass Anforderungen mit einer bestimmten Erweiterung ihre Middleware erreichen.However, you only want requests with a given extension to reach your middleware. Dies bietet Ihnen die gleiche Funktionalität wie bei Ihrem HTTP-Handler.That would give you the same functionality you had with your HTTP handler.

Eine Lösung besteht darin, die Pipeline für Anforderungen mit einer bestimmten Erweiterung mithilfe der Erweiterungsmethode zu verzweigen MapWhen .One solution is to branch the pipeline for requests with a given extension, using the MapWhen extension method. Dies geschieht in derselben Methode, in der Configure Sie die andere Middleware hinzufügen:You do this in the same Configure method where you add the other middleware:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    loggerFactory.AddConsole(Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseBrowserLink();
    }
    else
    {
        app.UseExceptionHandler("/Home/Error");
    }

    app.UseMyMiddleware();

    app.UseMyMiddlewareWithParams();

    var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
    var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
    app.UseMyMiddlewareWithParams(myMiddlewareOptions);
    app.UseMyMiddlewareWithParams(myMiddlewareOptions2);

    app.UseMyTerminatingMiddleware();

    // Create branch to the MyHandlerMiddleware. 
    // All requests ending in .report will follow this branch.
    app.MapWhen(
        context => context.Request.Path.ToString().EndsWith(".report"),
        appBranch => {
            // ... optionally add more middleware to this branch
            appBranch.UseMyHandler();
        });

    app.MapWhen(
        context => context.Request.Path.ToString().EndsWith(".context"),
        appBranch => {
            appBranch.UseHttpContextDemoMiddleware();
        });

    app.UseStaticFiles();

    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

MapWhenDiese Parameter werden von benötigt:MapWhen takes these parameters:

  1. Ein Lambda, das den annimmt HttpContext und zurückgibt, true Wenn die Anforderung den Branch nach unten durchlaufen soll.A lambda that takes the HttpContext and returns true if the request should go down the branch. Dies bedeutet, dass Sie Anforderungen verzweigen können, die nicht nur auf Grundlage ihrer Erweiterung basieren, sondern auch auf Anforderungs Headern, Abfrage Zeichen folgen Parametern usw.This means you can branch requests not just based on their extension, but also on request headers, query string parameters, etc.

  2. Ein Lambda, das eine annimmt IApplicationBuilder und alle Middleware für den Branch hinzufügt.A lambda that takes an IApplicationBuilder and adds all the middleware for the branch. Dies bedeutet, dass Sie der Verzweigung vor ihrer handlermiddleware zusätzliche Middleware hinzufügen können.This means you can add additional middleware to the branch in front of your handler middleware.

Der Pipeline hinzugefügte Middleware, bevor die Verzweigung für alle Anforderungen aufgerufen wird. die Verzweigung wirkt sich nicht auf diese aus.Middleware added to the pipeline before the branch will be invoked on all requests; the branch will have no impact on them.

Laden von Middleware-Optionen mit dem Options MusterLoading middleware options using the options pattern

Einige Module und Handler verfügen über Konfigurationsoptionen, die in Web.configgespeichert werden. In ASP.net Core jedoch anstelle Web.configein neues Konfigurations Modell verwendet.Some modules and handlers have configuration options that are stored in Web.config. However, in ASP.NET Core a new configuration model is used in place of Web.config.

Das neue Konfigurationssystem bietet Ihnen folgende Optionen, um dieses Problem zu beheben:The new configuration system gives you these options to solve this:

  1. Erstellen Sie eine Klasse zum Speichern der Middleware-Optionen, z. b.:Create a class to hold your middleware options, for example:

    public class MyMiddlewareOptions
    {
        public string Param1 { get; set; }
        public string Param2 { get; set; }
    }
    
  2. Speichern der OptionswerteStore the option values

    Mit dem Konfigurationssystem können Sie Optionswerte beliebig speichern.The configuration system allows you to store option values anywhere you want. Bei den meisten Websites wird jedoch appsettings.jsonverwendet, daher wird dieser Ansatz verwendet:However, most sites use appsettings.json, so we'll take that approach:

    {
      "MyMiddlewareOptionsSection": {
        "Param1": "Param1Value",
        "Param2": "Param2Value"
      }
    }
    

    Mymiddlewareoptionssection hier ist ein Abschnitts Name.MyMiddlewareOptionsSection here is a section name. Er muss nicht mit dem Namen der Options Klasse identisch sein.It doesn't have to be the same as the name of your options class.

  3. Zuordnen der Optionswerte zur Options-KlasseAssociate the option values with the options class

    Das Options Muster verwendet das Framework für die Abhängigkeitsinjektion von ASP.net Core, um den Options Typ (z. b. MyMiddlewareOptions ) einem- MyMiddlewareOptions Objekt zuzuordnen, das über die tatsächlichen Optionen verfügt.The options pattern uses ASP.NET Core's dependency injection framework to associate the options type (such as MyMiddlewareOptions) with a MyMiddlewareOptions object that has the actual options.

    Aktualisieren Sie Ihre Startup Klasse:Update your Startup class:

    1. Wenn Sie appsettings.jsonverwenden, fügen Sie es dem Konfigurations-Generator im- Startup Konstruktor hinzu:If you're using appsettings.json, add it to the configuration builder in the Startup constructor:

      public Startup(IHostingEnvironment env)
      {
          var builder = new ConfigurationBuilder()
              .SetBasePath(env.ContentRootPath)
              .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
              .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
              .AddEnvironmentVariables();
          Configuration = builder.Build();
      }
      
    2. Konfigurieren Sie den Options Dienst:Configure the options service:

      public void ConfigureServices(IServiceCollection services)
      {
          // Setup options service
          services.AddOptions();
      
          // Load options from section "MyMiddlewareOptionsSection"
          services.Configure<MyMiddlewareOptions>(
              Configuration.GetSection("MyMiddlewareOptionsSection"));
      
          // Add framework services.
          services.AddMvc();
      }
      
    3. Ordnen Sie die Optionen der Options-Klasse zu:Associate your options with your options class:

      public void ConfigureServices(IServiceCollection services)
      {
          // Setup options service
          services.AddOptions();
      
          // Load options from section "MyMiddlewareOptionsSection"
          services.Configure<MyMiddlewareOptions>(
              Configuration.GetSection("MyMiddlewareOptionsSection"));
      
          // Add framework services.
          services.AddMvc();
      }
      
  4. Fügen Sie die Optionen in ihren Middleware-Konstruktor ein.Inject the options into your middleware constructor. Dies ähnelt dem Einfügen von Optionen in einen Controller.This is similar to injecting options into a controller.

    public class MyMiddlewareWithParams
    {
        private readonly RequestDelegate _next;
        private readonly MyMiddlewareOptions _myMiddlewareOptions;
    
        public MyMiddlewareWithParams(RequestDelegate next,
            IOptions<MyMiddlewareOptions> optionsAccessor)
        {
            _next = next;
            _myMiddlewareOptions = optionsAccessor.Value;
        }
    
        public async Task Invoke(HttpContext context)
        {
            // Do something with context near the beginning of request processing
            // using configuration in _myMiddlewareOptions
    
            await _next.Invoke(context);
    
            // Do something with context near the end of request processing
            // using configuration in _myMiddlewareOptions
        }
    }
    

    Die usemiddleware -Erweiterungsmethode, mit der die Middleware hinzugefügt wird, IApplicationBuilder übernimmt die Abhängigkeitsinjektion.The UseMiddleware extension method that adds your middleware to the IApplicationBuilder takes care of dependency injection.

    Dies ist nicht auf- IOptions Objekte beschränkt.This isn't limited to IOptions objects. Alle anderen Objekte, die von der Middleware benötigt werden, können auf diese Weise eingefügt werden.Any other object that your middleware requires can be injected this way.

Laden von Middleware-Optionen durch direkte InjektionLoading middleware options through direct injection

Das Options Muster hat den Vorteil, dass es eine lose Kopplung zwischen Options Werten und ihren Consumern erzeugt.The options pattern has the advantage that it creates loose coupling between options values and their consumers. Nachdem Sie eine Options Klasse mit den tatsächlichen Options Werten verknüpft haben, kann jede andere Klasse über das Framework für die Abhängigkeitsinjektion Zugriff auf die Optionen erhalten.Once you've associated an options class with the actual options values, any other class can get access to the options through the dependency injection framework. Es ist nicht erforderlich, Optionswerte zu übergeben.There's no need to pass around options values.

Dies wird jedoch unterbrochen, wenn Sie dieselbe Middleware zweimal mit unterschiedlichen Optionen verwenden möchten.This breaks down though if you want to use the same middleware twice, with different options. Beispielsweise eine Autorisierungs Middleware, die in verschiedenen Verzweigungen verwendet wird und verschiedene Rollen zulässt.For example an authorization middleware used in different branches allowing different roles. Sie können der One Options-Klasse nicht zwei verschiedene Options Objekte zuordnen.You can't associate two different options objects with the one options class.

Die Lösung besteht darin, die Options Objekte mit den tatsächlichen Options Werten in Ihrer Startup Klasse zu erhalten und diese direkt an jede Instanz Ihrer Middleware zu übergeben.The solution is to get the options objects with the actual options values in your Startup class and pass those directly to each instance of your middleware.

  1. Fügen Sie einen zweiten Schlüssel zum appsettings.js hinzu.Add a second key to appsettings.json

    Verwenden Sie zum Hinzufügen eines zweiten Satzes von Optionen zum appsettings.js Datei einen neuen Schlüssel, um ihn eindeutig zu identifizieren:To add a second set of options to the appsettings.json file, use a new key to uniquely identify it:

    {
      "MyMiddlewareOptionsSection2": {
        "Param1": "Param1Value2",
        "Param2": "Param2Value2"
      },
      "MyMiddlewareOptionsSection": {
        "Param1": "Param1Value",
        "Param2": "Param2Value"
      }
    }
    
  2. Rufen Sie Optionswerte ab, und übergeben Sie Sie an die Middleware.Retrieve options values and pass them to middleware. Die Use... Erweiterungsmethode (die die Middleware zur Pipeline hinzufügt) ist ein logischer Speicherort, um die Optionswerte zu übergeben:The Use... extension method (which adds your middleware to the pipeline) is a logical place to pass in the option values:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();
    
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }
    
        app.UseMyMiddleware();
    
        app.UseMyMiddlewareWithParams();
    
        var myMiddlewareOptions = Configuration.GetSection("MyMiddlewareOptionsSection").Get<MyMiddlewareOptions>();
        var myMiddlewareOptions2 = Configuration.GetSection("MyMiddlewareOptionsSection2").Get<MyMiddlewareOptions>();
        app.UseMyMiddlewareWithParams(myMiddlewareOptions);
        app.UseMyMiddlewareWithParams(myMiddlewareOptions2);
    
        app.UseMyTerminatingMiddleware();
    
        // Create branch to the MyHandlerMiddleware. 
        // All requests ending in .report will follow this branch.
        app.MapWhen(
            context => context.Request.Path.ToString().EndsWith(".report"),
            appBranch => {
                // ... optionally add more middleware to this branch
                appBranch.UseMyHandler();
            });
    
        app.MapWhen(
            context => context.Request.Path.ToString().EndsWith(".context"),
            appBranch => {
                appBranch.UseHttpContextDemoMiddleware();
            });
    
        app.UseStaticFiles();
    
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
    
  3. Aktivieren Sie die Middleware, um einen Optionsparameter zu verwenden.Enable middleware to take an options parameter. Stellen Sie eine Überladung der Use... Erweiterungsmethode bereit (die den options-Parameter annimmt und Sie an übergibt UseMiddleware ).Provide an overload of the Use... extension method (that takes the options parameter and passes it to UseMiddleware). Wenn UseMiddleware mit Parametern aufgerufen wird, übergibt es die Parameter an Ihren Middleware-Konstruktor, wenn das Middleware-Objekt instanziiert wird.When UseMiddleware is called with parameters, it passes the parameters to your middleware constructor when it instantiates the middleware object.

    public static class MyMiddlewareWithParamsExtensions
    {
        public static IApplicationBuilder UseMyMiddlewareWithParams(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<MyMiddlewareWithParams>();
        }
    
        public static IApplicationBuilder UseMyMiddlewareWithParams(
            this IApplicationBuilder builder, MyMiddlewareOptions myMiddlewareOptions)
        {
            return builder.UseMiddleware<MyMiddlewareWithParams>(
                new OptionsWrapper<MyMiddlewareOptions>(myMiddlewareOptions));
        }
    }
    

    Beachten Sie, dass dies das Options-Objekt in einem-Objekt umschließt OptionsWrapper .Note how this wraps the options object in an OptionsWrapper object. Dies implementiert IOptions , wie vom Middleware-Konstruktor erwartet.This implements IOptions, as expected by the middleware constructor.

Migrieren zum neuen HttpContextMigrating to the new HttpContext

Sie haben bereits gesehen, dass die- Invoke Methode in der Middleware einen Parameter des Typs annimmt HttpContext :You saw earlier that the Invoke method in your middleware takes a parameter of type HttpContext:

public async Task Invoke(HttpContext context)

HttpContextwurde in ASP.net Core erheblich geändert.HttpContext has significantly changed in ASP.NET Core. In diesem Abschnitt wird gezeigt, wie die am häufigsten verwendeten Eigenschaften von System. Web. HttpContext in den neuen übersetzt werden Microsoft.AspNetCore.Http.HttpContext .This section shows how to translate the most commonly used properties of System.Web.HttpContext to the new Microsoft.AspNetCore.Http.HttpContext.

HttpContextHttpContext

HttpContext. Items übersetzt in:HttpContext.Items translates to:

IDictionary<object, object> items = httpContext.Items;

Eindeutige Anforderungs-ID (kein System. Web. HttpContext-Pendant)Unique request ID (no System.Web.HttpContext counterpart)

Gibt Ihnen eine eindeutige ID für jede Anforderung.Gives you a unique id for each request. Sehr nützlich, um in Ihre Protokolle einzubeziehen.Very useful to include in your logs.

string requestId = httpContext.TraceIdentifier;

HttpContext. RequestHttpContext.Request

HttpContext. Request. HttpMethod übersetzt in:HttpContext.Request.HttpMethod translates to:

string httpMethod = httpContext.Request.Method;

HttpContext. Request. QueryString übersetzt in:HttpContext.Request.QueryString translates to:

IQueryCollection queryParameters = httpContext.Request.Query;

// If no query parameter "key" used, values will have 0 items
// If single value used for a key (...?key=v1), values will have 1 item ("v1")
// If key has multiple values (...?key=v1&key=v2), values will have 2 items ("v1" and "v2")
IList<string> values = queryParameters["key"];

// If no query parameter "key" used, value will be ""
// If single value used for a key (...?key=v1), value will be "v1"
// If key has multiple values (...?key=v1&key=v2), value will be "v1,v2"
string value = queryParameters["key"].ToString();

" HttpContext. Request. URL " und " HttpContext. Request. RawUrl " übersetzt in:HttpContext.Request.Url and HttpContext.Request.RawUrl translate to:

// using Microsoft.AspNetCore.Http.Extensions;
var url = httpContext.Request.GetDisplayUrl();

HttpContext. Request. IsSecureConnection übersetzt in:HttpContext.Request.IsSecureConnection translates to:

var isSecureConnection = httpContext.Request.IsHttps;

HttpContext. Request. UserHostAddress übersetzt in:HttpContext.Request.UserHostAddress translates to:

var userHostAddress = httpContext.Connection.RemoteIpAddress?.ToString();

HttpContext. Request. Cookie s übersetzt in:HttpContext.Request.Cookies translates to:

IRequestCookieCollection cookies = httpContext.Request.Cookies;
string unknownCookieValue = cookies["unknownCookie"]; // will be null (no exception)
string knownCookieValue = cookies["cookie1name"];     // will be actual value

HttpContext. Request. RequestContext. RouteData übersetzt in:HttpContext.Request.RequestContext.RouteData translates to:

var routeValue = httpContext.GetRouteValue("key");

HttpContext. Request. Headers übersetzt in:HttpContext.Request.Headers translates to:

// using Microsoft.AspNetCore.Http.Headers;
// using Microsoft.Net.Http.Headers;

IHeaderDictionary headersDictionary = httpContext.Request.Headers;

// GetTypedHeaders extension method provides strongly typed access to many headers
var requestHeaders = httpContext.Request.GetTypedHeaders();
CacheControlHeaderValue cacheControlHeaderValue = requestHeaders.CacheControl;

// For unknown header, unknownheaderValues has zero items and unknownheaderValue is ""
IList<string> unknownheaderValues = headersDictionary["unknownheader"];
string unknownheaderValue = headersDictionary["unknownheader"].ToString();

// For known header, knownheaderValues has 1 item and knownheaderValue is the value
IList<string> knownheaderValues = headersDictionary[HeaderNames.AcceptLanguage];
string knownheaderValue = headersDictionary[HeaderNames.AcceptLanguage].ToString();

HttpContext. Request. UserAgent übersetzt in:HttpContext.Request.UserAgent translates to:

string userAgent = headersDictionary[HeaderNames.UserAgent].ToString();

HttpContext. Request. UrlReferrer übersetzt in:HttpContext.Request.UrlReferrer translates to:

string urlReferrer = headersDictionary[HeaderNames.Referer].ToString();

HttpContext. Request. ContentType übersetzt in:HttpContext.Request.ContentType translates to:

// using Microsoft.Net.Http.Headers;

MediaTypeHeaderValue mediaHeaderValue = requestHeaders.ContentType;
string contentType = mediaHeaderValue?.MediaType.ToString();   // ex. application/x-www-form-urlencoded
string contentMainType = mediaHeaderValue?.Type.ToString();    // ex. application
string contentSubType = mediaHeaderValue?.SubType.ToString();  // ex. x-www-form-urlencoded

System.Text.Encoding requestEncoding = mediaHeaderValue?.Encoding;

HttpContext. Request. Form übersetzt in:HttpContext.Request.Form translates to:

if (httpContext.Request.HasFormContentType)
{
    IFormCollection form;

    form = httpContext.Request.Form; // sync
    // Or
    form = await httpContext.Request.ReadFormAsync(); // async

    string firstName = form["firstname"];
    string lastName = form["lastname"];
}

Warnung

Liest Formular Werte nur, wenn der Sub-Inhaltstyp " x-www-form-urlencoded " oder " Form-Data" ist.Read form values only if the content sub type is x-www-form-urlencoded or form-data.

HttpContext. Request. InputStream übersetzt in:HttpContext.Request.InputStream translates to:

string inputBody;
using (var reader = new System.IO.StreamReader(
    httpContext.Request.Body, System.Text.Encoding.UTF8))
{
    inputBody = reader.ReadToEnd();
}

Warnung

Verwenden Sie diesen Code nur in einer Handlertyp-Middleware am Ende einer Pipeline.Use this code only in a handler type middleware, at the end of a pipeline.

Sie können den unformatierten Text nur einmal pro Anforderung lesen.You can read the raw body as shown above only once per request. Middleware, die versucht, den Text nach dem ersten Lesevorgang zu lesen, liest einen leeren Text.Middleware trying to read the body after the first read will read an empty body.

Dies gilt nicht für das Lesen eines Formulars wie zuvor gezeigt, da dies aus einem Puffer erfolgt.This doesn't apply to reading a form as shown earlier, because that's done from a buffer.

HttpContext. ResponseHttpContext.Response

" HttpContext. Response. Status " und " HttpContext. Response. StatusDescription " übersetzt in:HttpContext.Response.Status and HttpContext.Response.StatusDescription translate to:

// using Microsoft.AspNetCore.Http;
httpContext.Response.StatusCode = StatusCodes.Status200OK;

" HttpContext. Response. ContentEncoding " und " HttpContext. Response. ContentType " übersetzen in:HttpContext.Response.ContentEncoding and HttpContext.Response.ContentType translate to:

// using Microsoft.Net.Http.Headers;
var mediaType = new MediaTypeHeaderValue("application/json");
mediaType.Encoding = System.Text.Encoding.UTF8;
httpContext.Response.ContentType = mediaType.ToString();

" HttpContext. Response. ContentType " selbst übersetzt ebenfalls Folgendes:HttpContext.Response.ContentType on its own also translates to:

httpContext.Response.ContentType = "text/html";

HttpContext. Response. Output übersetzt in:HttpContext.Response.Output translates to:

string responseContent = GetResponseContent();
await httpContext.Response.WriteAsync(responseContent);

HttpContext. Response. TransmitFileHttpContext.Response.TransmitFile

Die Einrichtung einer Datei wird hiererläutert.Serving up a file is discussed here.

HttpContext. Response. HeadersHttpContext.Response.Headers

Das Senden von Antwort Headern ist kompliziert, wenn Sie Sie nach dem Schreiben in den Antworttext festlegen, werden Sie nicht gesendet.Sending response headers is complicated by the fact that if you set them after anything has been written to the response body, they will not be sent.

Die Lösung besteht darin, eine Rückruf Methode festzulegen, die vor dem Schreiben in die Antwort direkt aufgerufen wird.The solution is to set a callback method that will be called right before writing to the response starts. Dies wird am Anfang der Invoke Methode in ihrer Middleware am besten erreicht.This is best done at the start of the Invoke method in your middleware. Diese Rückruf Methode legt die Antwortheader fest.It's this callback method that sets your response headers.

Der folgende Code legt eine Rückruf Methode namensfest SetHeaders :The following code sets a callback method called SetHeaders:

public async Task Invoke(HttpContext httpContext)
{
    // ...
    httpContext.Response.OnStarting(SetHeaders, state: httpContext);

Die SetHeaders Rückruf Methode würde wie folgt aussehen:The SetHeaders callback method would look like this:

// using Microsoft.AspNet.Http.Headers;
// using Microsoft.Net.Http.Headers;

private Task SetHeaders(object context)
{
    var httpContext = (HttpContext)context;

    // Set header with single value
    httpContext.Response.Headers["ResponseHeaderName"] = "headerValue";

    // Set header with multiple values
    string[] responseHeaderValues = new string[] { "headerValue1", "headerValue1" };
    httpContext.Response.Headers["ResponseHeaderName"] = responseHeaderValues;

    // Translating ASP.NET 4's HttpContext.Response.RedirectLocation  
    httpContext.Response.Headers[HeaderNames.Location] = "http://www.example.com";
    // Or
    httpContext.Response.Redirect("http://www.example.com");

    // GetTypedHeaders extension method provides strongly typed access to many headers
    var responseHeaders = httpContext.Response.GetTypedHeaders();

    // Translating ASP.NET 4's HttpContext.Response.CacheControl 
    responseHeaders.CacheControl = new CacheControlHeaderValue
    {
        MaxAge = new System.TimeSpan(365, 0, 0, 0)
        // Many more properties available 
    };

    // If you use .NET Framework 4.6+, Task.CompletedTask will be a bit faster
    return Task.FromResult(0);
}

HttpContext. Response. Cookie HymnenHttpContext.Response.Cookies

Cookies wechseln zum Browser in einem Set-Response Cookie - Header.Cookies travel to the browser in a Set-Cookie response header. Daher cookie erfordert das Senden von s denselben Rückruf wie zum Senden von Antwort Headern:As a result, sending cookies requires the same callback as used for sending response headers:

public async Task Invoke(HttpContext httpContext)
{
    // ...
    httpContext.Response.OnStarting(SetCookies, state: httpContext);
    httpContext.Response.OnStarting(SetHeaders, state: httpContext);

Die SetCookies Rückruf Methode würde wie folgt aussehen:The SetCookies callback method would look like the following:

private Task SetCookies(object context)
{
    var httpContext = (HttpContext)context;

    IResponseCookies responseCookies = httpContext.Response.Cookies;

    responseCookies.Append("cookie1name", "cookie1value");
    responseCookies.Append("cookie2name", "cookie2value",
        new CookieOptions { Expires = System.DateTime.Now.AddDays(5), HttpOnly = true });

    // If you use .NET Framework 4.6+, Task.CompletedTask will be a bit faster
    return Task.FromResult(0); 
}

Weitere RessourcenAdditional resources