Eseguire la migrazione di gestori e moduli HTTP a ASP.NET Core middlewareMigrate HTTP handlers and modules to ASP.NET Core middleware

Questo articolo illustra come eseguire la migrazione di moduli e gestori HTTP ASP.NET esistenti da System. webserver a middlewareASP.NET Core.This article shows how to migrate existing ASP.NET HTTP modules and handlers from system.webserver to ASP.NET Core middleware.

Moduli e gestori rivisitatiModules and handlers revisited

Prima di procedere con ASP.NET Core middleware, è necessario riepilogare il funzionamento dei moduli e gestori HTTP:Before proceeding to ASP.NET Core middleware, let's first recap how HTTP modules and handlers work:

Gestore moduli

I gestori sono:Handlers are:

  • Classi che implementano IHttpHandlerClasses that implement IHttpHandler

  • Utilizzato per gestire le richieste con un nome file o un'estensione specifica, ad esempio . reportUsed to handle requests with a given file name or extension, such as .report

  • Configurazione in Web. configConfigured in Web.config

I moduli sono:Modules are:

  • Classi che implementano IHttpModuleClasses that implement IHttpModule

  • Richiamato per ogni richiestaInvoked for every request

  • In grado di eseguire il cortocircuito (arrestare l'ulteriore elaborazione di una richiesta)Able to short-circuit (stop further processing of a request)

  • In grado di aggiungere alla risposta HTTP o di crearne di propriAble to add to the HTTP response, or create their own

  • Configurazione in Web. configConfigured in Web.config

L'ordine in cui i moduli elaborano le richieste in ingresso è determinato da:The order in which modules process incoming requests is determined by:

  1. Il ciclo di vita dell'applicazione, ovvero eventi di serie generati da ASP.NET: BeginRequest, AuthenticateRequeste così via. Ogni modulo può creare un gestore per uno o più eventi.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. Per lo stesso evento, l'ordine in cui sono configurati in Web. config.For the same event, the order in which they're configured in Web.config.

Oltre ai moduli, è possibile aggiungere i gestori per gli eventi del ciclo di vita al file Global.asax.cs .In addition to modules, you can add handlers for the life cycle events to your Global.asax.cs file. Questi gestori vengono eseguiti dopo i gestori nei moduli configurati.These handlers run after the handlers in the configured modules.

Da gestori e moduli al middlewareFrom handlers and modules to middleware

Il middleware è più semplice dei moduli e gestori HTTP:Middleware are simpler than HTTP modules and handlers:

  • Moduli, gestori, Global.asax.cs, Web. config (eccetto la configurazione di IIS) e il ciclo di vita dell'applicazione sono finitiModules, handlers, Global.asax.cs, Web.config (except for IIS configuration) and the application life cycle are gone

  • I ruoli di moduli e gestori sono stati rilevati dal middlewareThe roles of both modules and handlers have been taken over by middleware

  • Il middleware viene configurato usando codice anziché in Web. configMiddleware are configured using code rather than in Web.config

  • La creazione di rami della pipeline consente di inviare richieste a un middleware specifico, in base non solo all'URL, ma anche alle intestazioni delle richieste, alle stringhe di query e così via.Pipeline branching lets you send requests to specific middleware, based on not only the URL but also on request headers, query strings, etc.

Il middleware è molto simile ai moduli:Middleware are very similar to modules:

Il middleware e i moduli vengono elaborati in un ordine diverso:Middleware and modules are processed in a different order:

Middleware

Si noti come nell'immagine precedente il middleware di autenticazione ha corto circuito della richiesta.Note how in the image above, the authentication middleware short-circuited the request.

Migrazione del codice del modulo al middlewareMigrating module code to middleware

Un modulo HTTP esistente sarà simile al seguente: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.
        }
    }
}

Come illustrato nella pagina middleware , un middleware ASP.NET Core è una classe che espone un metodo di Invoke che accetta un HttpContext e restituisce un 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. Il nuovo middleware sarà simile al seguente: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>();
        }
    }
}

Il modello middleware precedente è stato ricavato dalla sezione sulla scrittura del middleware.The preceding middleware template was taken from the section on writing middleware.

La classe helper MyMiddlewareExtensions rende più semplice la configurazione del middleware nella classe Startup.The MyMiddlewareExtensions helper class makes it easier to configure your middleware in your Startup class. Il metodo UseMyMiddleware aggiunge la classe middleware alla pipeline della richiesta.The UseMyMiddleware method adds your middleware class to the request pipeline. I servizi richiesti dal middleware vengono inseriti nel costruttore del middleware.Services required by the middleware get injected in the middleware's constructor.

Il modulo potrebbe terminare una richiesta, ad esempio se l'utente non è autorizzato: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;
    }
}

Un middleware gestisce questa operazione non chiamando Invoke sul middleware successivo nella pipeline.A middleware handles this by not calling Invoke on the next middleware in the pipeline. Tenere presente che questa operazione non termina completamente la richiesta, perché i middleware precedenti verranno comunque richiamati quando la risposta ripercorre la pipeline.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.
}

Quando si esegue la migrazione della funzionalità del modulo al nuovo middleware, è possibile che il codice non venga compilato perché la classe HttpContext è cambiata in modo significativo nell'ASP.NET Core.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. Successivamente, verrà illustrato come eseguire la migrazione al nuovo ASP.NET Core HttpContext.Later on, you'll see how to migrate to the new ASP.NET Core HttpContext.

Migrazione dell'inserimento del modulo nella pipeline delle richiesteMigrating module insertion into the request pipeline

I moduli HTTP vengono in genere aggiunti alla pipeline di richiesta tramite Web. config: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>

Per convertirlo, aggiungere il nuovo middleware alla pipeline delle richieste nella classe 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?}");
    });
}

Il punto esatto della pipeline in cui si inserisce il nuovo middleware dipende dall'evento gestito come modulo (BeginRequest, EndRequeste così via) e dall'ordine nell'elenco dei moduli 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.

Come indicato in precedenza, non esiste alcun ciclo di vita dell'applicazione in ASP.NET Core e l'ordine in cui le risposte vengono elaborate dal middleware differisce dall'ordine usato dai moduli.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. Questo potrebbe rendere più complessa la decisione di ordinamento.This could make your ordering decision more challenging.

Se l'ordinamento diventa un problema, è possibile suddividere il modulo in più componenti middleware che possono essere ordinati in modo indipendente.If ordering becomes a problem, you could split your module into multiple middleware components that can be ordered independently.

Migrazione del codice del gestore al middlewareMigrating handler code to middleware

Un gestore HTTP ha un aspetto simile al seguente: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";
        }
    }
}

Nel progetto di ASP.NET Core, è necessario tradurlo in un middleware simile al seguente: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>();
        }
    }
}

Questo middleware è molto simile al middleware corrispondente ai moduli.This middleware is very similar to the middleware corresponding to modules. L'unica differenza reale è che in questo caso non viene chiamata _next.Invoke(context).The only real difference is that here there's no call to _next.Invoke(context). Questa operazione è sensata, perché il gestore si trova alla fine della pipeline delle richieste, quindi non ci sarà alcun middleware successivo da richiamare.That makes sense, because the handler is at the end of the request pipeline, so there will be no next middleware to invoke.

Migrazione dell'inserimento del gestore nella pipeline delle richiesteMigrating handler insertion into the request pipeline

La configurazione di un gestore HTTP viene eseguita in Web. config e ha un aspetto simile al seguente: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>

È possibile eseguire la conversione aggiungendo il middleware del nuovo gestore alla pipeline delle richieste nella classe Startup, in modo analogo a middleware convertito da moduli.You could convert this by adding your new handler middleware to the request pipeline in your Startup class, similar to middleware converted from modules. Il problema con questo approccio è che invia tutte le richieste al nuovo middleware del gestore.The problem with that approach is that it would send all requests to your new handler middleware. Tuttavia, si desidera che le richieste con una determinata estensione raggiungano il middleware.However, you only want requests with a given extension to reach your middleware. Questo consentirebbe di ottenere le stesse funzionalità con il gestore HTTP.That would give you the same functionality you had with your HTTP handler.

Una soluzione consiste nell'eseguire il branching della pipeline per le richieste con una determinata estensione, usando il metodo di estensione MapWhen.One solution is to branch the pipeline for requests with a given extension, using the MapWhen extension method. Questa operazione viene eseguita nello stesso Configure metodo in cui si aggiunge l'altro middleware: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?}");
    });
}

MapWhen accetta i parametri seguenti:MapWhen takes these parameters:

  1. Espressione lambda che accetta il HttpContext e restituisce true se la richiesta deve andare al di sotto del ramo.A lambda that takes the HttpContext and returns true if the request should go down the branch. Ciò significa che è possibile creare branch richieste non solo in base alla relativa estensione, ma anche su intestazioni di richiesta, parametri della stringa di query e così via.This means you can branch requests not just based on their extension, but also on request headers, query string parameters, etc.

  2. Espressione lambda che accetta un IApplicationBuilder e aggiunge tutto il middleware per il ramo.A lambda that takes an IApplicationBuilder and adds all the middleware for the branch. Ciò significa che è possibile aggiungere un middleware aggiuntivo al ramo davanti al middleware del gestore.This means you can add additional middleware to the branch in front of your handler middleware.

Middleware aggiunto alla pipeline prima che il ramo venga richiamato in tutte le richieste; il ramo non avrà alcun effetto su di essi.Middleware added to the pipeline before the branch will be invoked on all requests; the branch will have no impact on them.

Caricamento delle opzioni del middleware usando il modello di opzioniLoading middleware options using the options pattern

Alcuni moduli e gestori hanno opzioni di configurazione archiviate in Web. config. Tuttavia, in ASP.NET Core viene usato un nuovo modello di configurazione al posto di Web. config.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.

Il nuovo sistema di configurazione offre le opzioni seguenti per risolvere il problema:The new configuration system gives you these options to solve this:

  1. Creare una classe che contenga le opzioni del middleware, ad esempio:Create a class to hold your middleware options, for example:

    public class MyMiddlewareOptions
    {
        public string Param1 { get; set; }
        public string Param2 { get; set; }
    }
    
  2. Archiviare i valori delle opzioniStore the option values

    Il sistema di configurazione consente di archiviare i valori delle opzioni ovunque si desideri.The configuration system allows you to store option values anywhere you want. Tuttavia, la maggior parte dei siti USA appSettings. JSON, quindi questo approccio verrà adottato:However, most sites use appsettings.json, so we'll take that approach:

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

    MyMiddlewareOptionsSection è il nome di una sezione.MyMiddlewareOptionsSection here is a section name. Non deve corrispondere al nome della classe Options.It doesn't have to be the same as the name of your options class.

  3. Associare i valori delle opzioni alla classe OptionsAssociate the option values with the options class

    Il modello di opzioni usa il Framework di inserimento delle dipendenze di ASP.NET Core per associare il tipo di opzioni, ad esempio MyMiddlewareOptions, a un oggetto MyMiddlewareOptions con le opzioni effettive.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.

    Aggiornare la classe Startup:Update your Startup class:

    1. Se si usa appSettings. JSON, aggiungerlo al generatore di configurazione nel costruttore Startup: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. Configurare il servizio opzioni: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. Associare le opzioni alla classe Options: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. Inserire le opzioni nel costruttore del middleware.Inject the options into your middleware constructor. Questa operazione è simile all'inserimento di opzioni in un 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
        }
    }
    

    Il metodo di estensione UseMiddleware che aggiunge il middleware al IApplicationBuilder si occupa dell'inserimento delle dipendenze.The UseMiddleware extension method that adds your middleware to the IApplicationBuilder takes care of dependency injection.

    Questo non è limitato agli oggetti IOptions.This isn't limited to IOptions objects. Tutti gli altri oggetti richiesti dal middleware possono essere inseriti in questo modo.Any other object that your middleware requires can be injected this way.

Caricamento delle opzioni del middleware tramite l'inserimento direttoLoading middleware options through direct injection

Il modello Options presenta il vantaggio di creare un accoppiamento libero tra i valori delle opzioni e i relativi consumer.The options pattern has the advantage that it creates loose coupling between options values and their consumers. Una volta associata una classe Options con i valori effettivi delle opzioni, qualsiasi altra classe può ottenere l'accesso alle opzioni tramite il Framework di inserimento delle dipendenze.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. Non è necessario passare i valori delle opzioni.There's no need to pass around options values.

Questa operazione si interrompe anche se si vuole usare lo stesso middleware due volte, con opzioni diverse.This breaks down though if you want to use the same middleware twice, with different options. Ad esempio un middleware di autorizzazione usato in rami diversi che consentono ruoli diversi.For example an authorization middleware used in different branches allowing different roles. Non è possibile associare due oggetti Options diversi a una classe di opzioni.You can't associate two different options objects with the one options class.

La soluzione consiste nell'ottenere gli oggetti Options con i valori effettivi delle opzioni nella classe Startup e passarli direttamente a ogni istanza del middleware.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. Aggiungere una seconda chiave a appSettings. JSONAdd a second key to appsettings.json

    Per aggiungere un secondo set di opzioni al file appSettings. JSON , usare una nuova chiave per identificarla in modo univoco: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. Recuperare i valori delle opzioni e passarli al middleware.Retrieve options values and pass them to middleware. Il metodo di estensione Use... (che aggiunge il middleware alla pipeline) è una posizione logica per passare i valori delle opzioni: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. Consentire al middleware di prendere un parametro options.Enable middleware to take an options parameter. Fornire un overload del metodo di estensione Use... (che accetta il parametro options e lo passa a UseMiddleware).Provide an overload of the Use... extension method (that takes the options parameter and passes it to UseMiddleware). Quando UseMiddleware viene chiamato con parametri, passa i parametri al costruttore del middleware quando crea un'istanza dell'oggetto middleware.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));
        }
    }
    

    Si noti che in questo modo viene eseguito il wrapping dell'oggetto options in un oggetto OptionsWrapper.Note how this wraps the options object in an OptionsWrapper object. Implementa IOptions, come previsto dal costruttore del middleware.This implements IOptions, as expected by the middleware constructor.

Migrazione al nuovo HttpContextMigrating to the new HttpContext

Si è visto in precedenza che il metodo Invoke nel middleware accetta un parametro di tipo HttpContext:You saw earlier that the Invoke method in your middleware takes a parameter of type HttpContext:

public async Task Invoke(HttpContext context)

HttpContext è stato modificato in modo significativo nell'ASP.NET Core.HttpContext has significantly changed in ASP.NET Core. Questa sezione illustra come tradurre le proprietà più comunemente usate di System. Web. HttpContext nella nuova 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 viene convertito in:HttpContext.Items translates to:

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

ID richiesta univoco (nessuna controparte System. Web. HttpContext)Unique request ID (no System.Web.HttpContext counterpart)

Fornisce un ID univoco per ogni richiesta.Gives you a unique id for each request. Molto utile da includere nei log.Very useful to include in your logs.

string requestId = httpContext.TraceIdentifier;

HttpContext.RequestHttpContext.Request

HttpContext. Request. HttpMethod viene convertito in:HttpContext.Request.HttpMethod translates to:

string httpMethod = httpContext.Request.Method;

HttpContext. Request. QueryString viene convertito 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 e HttpContext. Request. RawUrl vengono convertiti in:HttpContext.Request.Url and HttpContext.Request.RawUrl translate to:

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

HttpContext. Request. IsSecureConnection viene convertito in:HttpContext.Request.IsSecureConnection translates to:

var isSecureConnection = httpContext.Request.IsHttps;

HttpContext. Request. UserHostAddress viene convertito in:HttpContext.Request.UserHostAddress translates to:

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

HttpContext. Request. cookies viene convertito 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 viene convertito in:HttpContext.Request.RequestContext.RouteData translates to:

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

HttpContext. Request. Headers viene convertito 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 viene convertito in:HttpContext.Request.UserAgent translates to:

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

HttpContext. Request. UrlReferrer viene convertito in:HttpContext.Request.UrlReferrer translates to:

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

HttpContext. Request. ContentType viene convertito 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 viene convertito 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"];
}

Avviso

Leggere i valori del modulo solo se il sottotipo di contenuto è x-www-form-urlencoded o form-data.Read form values only if the content sub type is x-www-form-urlencoded or form-data.

HttpContext. Request. InputStream viene convertito 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();
}

Avviso

Usare questo codice solo in un middleware di tipo gestore, alla fine di una pipeline.Use this code only in a handler type middleware, at the end of a pipeline.

È possibile leggere il corpo non elaborato come illustrato sopra solo una volta per ogni richiesta.You can read the raw body as shown above only once per request. Il middleware che tenta di leggere il corpo dopo la prima lettura leggerà un corpo vuoto.Middleware trying to read the body after the first read will read an empty body.

Questa operazione non si applica alla lettura di un modulo, come illustrato in precedenza, perché viene eseguita da un buffer.This doesn't apply to reading a form as shown earlier, because that's done from a buffer.

HttpContext.ResponseHttpContext.Response

HttpContext. Response. status e HttpContext. Response. StatusDescription vengono convertiti in:HttpContext.Response.Status and HttpContext.Response.StatusDescription translate to:

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

HttpContext. Response. ContentEncoding e HttpContext. Response. ContentType sono convertibili 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();

Anche HttpContext. Response. ContentType viene convertito in:HttpContext.Response.ContentType on its own also translates to:

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

HttpContext. Response. output viene convertito in:HttpContext.Response.Output translates to:

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

HttpContext. Response. TransmitFileHttpContext.Response.TransmitFile

Quiviene illustrato come servire un file.Serving up a file is discussed here.

HttpContext. Response. HeadersHttpContext.Response.Headers

L'invio di intestazioni di risposta è complicato dal fatto che, se si impostano elementi che sono stati scritti nel corpo della risposta, non verranno inviati.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.

La soluzione consiste nell'impostare un metodo di callback che verrà chiamato immediatamente prima di iniziare la scrittura della risposta.The solution is to set a callback method that will be called right before writing to the response starts. Questa operazione è ottimale all'inizio del metodo Invoke nel middleware.This is best done at the start of the Invoke method in your middleware. Si tratta di un metodo di callback che imposta le intestazioni della risposta.It's this callback method that sets your response headers.

Il codice seguente imposta un metodo di callback denominato SetHeaders:The following code sets a callback method called SetHeaders:

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

Il metodo di callback SetHeaders avrà un aspetto simile al seguente: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. cookiesHttpContext.Response.Cookies

I cookie passano al browser in un'intestazione di risposta set-cookie .Cookies travel to the browser in a Set-Cookie response header. Di conseguenza, l'invio di cookie richiede lo stesso callback utilizzato per inviare le intestazioni di risposta: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);

Il metodo di callback SetCookies avrà un aspetto simile al seguente: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); 
}

Risorse aggiuntiveAdditional resources