Eseguire la migrazione di gestori e moduli HTTP a middleware di ASP.NET Core

Questo articolo illustra come eseguire la migrazione di moduli e gestori HTTP ASP.NET esistenti da system.webserver a ASP.NET middleware Core.

Moduli e gestori rivisitati

Prima di procedere a ASP.NET middleware Core, si esaminerà prima di tutto il funzionamento dei moduli e dei gestori HTTP:

Gestore moduli

I gestori sono:

  • Classi che implementano IHttpHandler

  • Usato per gestire le richieste con un nome o un'estensione di file specificati, ad esempio .report

  • Configurato in Web.config

I moduli sono:

  • Classi che implementano IHttpModule

  • Richiamato per ogni richiesta

  • In grado di corto circuito (arrestare un'ulteriore elaborazione di una richiesta)

  • È possibile aggiungere alla risposta HTTP o crearne uno personalizzato

  • Configurato in Web.config

L'ordine in cui i moduli elaborano le richieste in ingresso è determinato da:

  1. Eventi di serie generati da ASP.NET, ad esempio BeginRequest e AuthenticateRequest. Per un elenco completo, vedere System.Web.HttpApplication. Ogni modulo può creare un gestore per uno o più eventi.

  2. Per lo stesso evento, l'ordine in cui sono configurati in Web.config.

Oltre ai moduli, è possibile aggiungere gestori per gli eventi del ciclo di vita al Global.asax.cs file. Questi gestori vengono eseguiti dopo i gestori nei moduli configurati.

Da gestori e moduli al middleware

Il middleware è più semplice rispetto ai moduli e ai gestori HTTP:

  • Moduli, gestori, Global.asax.cs, Web.config (ad eccezione della configurazione iis) e il ciclo di vita dell'applicazione non sono più disponibili

  • I ruoli di moduli e gestori sono stati acquisiti dal middleware

  • Il middleware viene configurato usando codice anziché in Web.config

  • La diramazione della pipeline consente di inviare richieste a middleware specifici, in base non solo all'URL, ma anche alle intestazioni delle richieste, alle stringhe di query e così via.
  • La diramazione della pipeline consente di inviare richieste a middleware specifici, in base non solo all'URL, ma anche alle intestazioni delle richieste, alle stringhe di query e così via.

Il middleware è molto simile ai moduli:

Il middleware e i moduli vengono elaborati in un ordine diverso:

  • L'ordine del middleware è basato sull'ordine in cui vengono inseriti nella pipeline di richiesta, mentre l'ordine dei moduli si basa principalmente sugli System.Web.HttpApplication eventi.

  • L'ordine del middleware per le risposte è il contrario rispetto a quello per le richieste, mentre l'ordine dei moduli è lo stesso per le richieste e le risposte

  • Vedere Creare una pipeline middleware con IApplicationBuilder

Il middleware di autorizzazione cortocircuita una richiesta per un utente che non è autorizzato. Una richiesta per la pagina Indice è consentita ed elaborata dal middleware MVC. Una richiesta di un report di vendita è consentita ed elaborata da un middleware del report personalizzato.

Si noti come nell'immagine precedente il middleware di autenticazione ha corto circuito la richiesta.

Migrazione del codice del modulo al middleware

Un modulo HTTP esistente sarà simile al seguente:

// 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 Invoke metodo che accetta e HttpContext restituisce un oggetto Task. Il nuovo middleware sarà simile al seguente:

// 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 tratto dalla sezione relativa alla scrittura del middleware.

La classe helper MyMiddlewareExtensions semplifica la configurazione del middleware nella Startup classe. Il UseMyMiddleware metodo aggiunge la classe middleware alla pipeline di richiesta. I servizi richiesti dal middleware vengono inseriti nel costruttore del middleware.

Il modulo potrebbe terminare una richiesta, ad esempio se l'utente non è autorizzato:

// 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 senza chiamare Invoke il middleware successivo nella pipeline. Tenere presente che questa operazione non termina completamente la richiesta, perché i middleware precedenti verranno comunque richiamati quando la risposta torna indietro nella 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 delle funzionalità del modulo al nuovo middleware, è possibile che il codice non venga compilato perché la HttpContext classe è cambiata in modo significativo in ASP.NET Core. Più avanti si vedrà come eseguire la migrazione alla nuova ASP.NET Core HttpContext.

Migrazione dell'inserimento del modulo nella pipeline di richiesta

I moduli HTTP vengono in genere aggiunti alla pipeline di richiesta usando 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>

Convertirlo aggiungendo il nuovo middleware alla pipeline di richiesta nella Startup classe :

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 nella pipeline in cui si inserisce il nuovo middleware dipende dall'evento gestito come modulo (BeginRequest, EndRequeste così via) e dal relativo ordine nell'elenco di moduli 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. Questo potrebbe rendere più difficile la decisione di ordinamento.

Se l'ordinamento diventa un problema, è possibile suddividere il modulo in più componenti middleware che possono essere ordinati in modo indipendente.

Migrazione del codice del gestore al middleware

Un gestore HTTP è simile al seguente:

// 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 ASP.NET Core, il middleware verrà convertito in un middleware simile al seguente:

// 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. L'unica differenza reale è che qui non c'è alcuna chiamata a _next.Invoke(context). Ciò ha senso, perché il gestore si trova alla fine della pipeline della richiesta, quindi non ci sarà alcun middleware successivo da richiamare.

Migrazione dell'inserimento del gestore nella pipeline di richiesta

La configurazione di un gestore HTTP viene eseguita in Web.config e ha un aspetto simile al seguente:

<?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 convertirlo aggiungendo il nuovo middleware del gestore alla pipeline di richiesta nella Startup classe, simile al middleware convertito dai moduli. Il problema con questo approccio è che invierebbe tutte le richieste al nuovo middleware del gestore. Tuttavia, si desidera che solo le richieste con una determinata estensione raggiungano il middleware. In questo modo è possibile usare la stessa funzionalità del gestore HTTP.

Una soluzione consiste nel diramare la pipeline per le richieste con una determinata estensione, usando il MapWhen metodo di estensione. Questa operazione viene eseguita nello stesso Configure metodo in cui si aggiunge l'altro 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 questi parametri:

  1. Espressione lambda che accetta HttpContext e restituisce true se la richiesta deve arrestare il ramo. Ciò significa che è possibile creare un ramo delle richieste non solo in base all'estensione, ma anche alle intestazioni delle richieste, ai parametri della stringa di query e così via.

  2. Espressione lambda che accetta e IApplicationBuilder aggiunge tutto il middleware per il ramo. Ciò significa che è possibile aggiungere altri middleware al ramo davanti al middleware del gestore.

Middleware aggiunto alla pipeline prima che il ramo venga richiamato su tutte le richieste; il ramo non avrà alcun impatto su di essi.

Caricamento delle opzioni del middleware tramite il modello di opzioni

Alcuni moduli e gestori dispongono di opzioni di configurazione archiviate in Web.config. Tuttavia, in ASP.NET Core viene usato un nuovo modello di configurazione al posto di Web.config.

Il nuovo sistema di configurazione offre queste opzioni per risolvere questo problema:

  • Inserire direttamente le opzioni nel middleware, come illustrato nella sezione successiva.

  • Usare il modello di opzioni:

  1. Creare una classe per contenere le opzioni del middleware, ad esempio:

    public class MyMiddlewareOptions
    {
        public string Param1 { get; set; }
        public string Param2 { get; set; }
    }
    
  2. Archiviare i valori delle opzioni

    Il sistema di configurazione consente di archiviare i valori delle opzioni ovunque si desideri. Tuttavia, la maggior parte dei siti usa appsettings.json, quindi si userà questo approccio:

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

    MyMiddlewareOptionsSection qui è un nome di sezione. Non deve essere uguale al nome della classe di opzioni.

  3. Associare i valori delle opzioni alla classe options

    Il modello di opzioni usa ASP.NET framework di inserimento delle dipendenze di Core per associare il tipo di opzioni (ad esempio MyMiddlewareOptions) a un MyMiddlewareOptions oggetto con le opzioni effettive.

    Aggiornare la Startup classe:

    1. Se si usa appsettings.json, aggiungerlo al generatore di configurazione nel Startup costruttore:

      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:

      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 di opzioni:

      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. Questo è simile all'inserimento di opzioni in un 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 all'oggetto si occupa dell'inserimento IApplicationBuilder delle dipendenze.

    Questo non è limitato agli IOptions oggetti. Qualsiasi altro oggetto richiesto dal middleware può essere inserito in questo modo.

Caricamento di opzioni middleware tramite inserimento diretto

Il modello di opzioni offre il vantaggio di creare un accoppiamento libero tra i valori delle opzioni e i relativi consumer. Dopo aver associato una classe di opzioni ai valori effettivi delle opzioni, qualsiasi altra classe può accedere alle opzioni tramite il framework di inserimento delle dipendenze. Non è necessario passare i valori delle opzioni.

Questo si suddivide anche se si vuole usare lo stesso middleware due volte, con opzioni diverse. Ad esempio, un middleware di autorizzazione usato in rami diversi che consentono ruoli diversi. Non è possibile associare due oggetti opzioni diversi a una classe di opzioni.

La soluzione consiste nel ottenere gli oggetti opzioni con i valori effettivi delle opzioni nella Startup classe e passarli direttamente a ogni istanza del middleware.

  1. Aggiungere una seconda chiave a appsettings.json

    Per aggiungere un secondo set di opzioni al appsettings.json file, usare una nuova chiave per identificarla in modo univoco:

    {
      "MyMiddlewareOptionsSection2": {
        "Param1": "Param1Value2",
        "Param2": "Param2Value2"
      },
      "MyMiddlewareOptionsSection": {
        "Param1": "Param1Value",
        "Param2": "Param2Value"
      }
    }
    
  2. Recuperare i valori delle opzioni e passarli al middleware. Il Use... metodo di estensione (che aggiunge il middleware alla pipeline) è una posizione logica da passare i valori delle opzioni:

    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. Abilitare il middleware per accettare un parametro di opzioni. Specificare un overload del Use... metodo di estensione che accetta il parametro options e lo passa a UseMiddleware. Quando UseMiddleware viene chiamato con parametri, passa i parametri al costruttore middleware quando crea un'istanza dell'oggetto middleware.

    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 come esegue il wrapping dell'oggetto options in un OptionsWrapper oggetto . Ciò implementa IOptions, come previsto dal costruttore middleware.

Migrazione al nuovo HttpContext

Si è visto in precedenza che il Invoke metodo nel middleware accetta un parametro di tipo HttpContext:

public async Task Invoke(HttpContext context)

HttpContext è cambiato significativamente in ASP.NET Core. In questa sezione viene illustrato come convertire le proprietà più usate di System.Web.HttpContext nel nuovo Microsoft.AspNetCore.Http.HttpContextoggetto .

HttpContext

HttpContext.Items si traduce in:

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

ID richiesta univoco (nessuna controparte System.Web.HttpContext)

Fornisce un ID univoco per ogni richiesta. Molto utile da includere nei log.

string requestId = httpContext.TraceIdentifier;

HttpContext.Request

HttpContext.Request.HttpMethod si traduce in:

string httpMethod = httpContext.Request.Method;

HttpContext.Request.QueryString si traduce in:

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 traducono in:

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

HttpContext.Request.IsSecureConnection viene convertito in:

var isSecureConnection = httpContext.Request.IsHttps;

HttpContext.Request.UserHostAddress si traduce in:

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

HttpContext.Request.Cookies si traduce in:

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 si traduce in:

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

HttpContext.Request.Headers si traduce in:

// 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 si traduce in:

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

HttpContext.Request.UrlReferrer si traduce in:

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

HttpContext.Request.ContentType si traduce in:

// 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:

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

Legge i valori del modulo solo se il sottotipo di contenuto è x-www-form-urlencoded o form-data.

HttpContext.Request.InputStream si traduce in:

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.

È possibile leggere il corpo non elaborato come illustrato in precedenza una sola volta per ogni richiesta. Il middleware che tenta di leggere il corpo dopo la prima lettura leggerà un corpo vuoto.

Ciò non si applica alla lettura di un modulo come illustrato in precedenza, perché questa operazione viene eseguita da un buffer.

HttpContext.Response

HttpContext.Response.Status e HttpContext.Response.StatusDescription si traducono in:

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

HttpContext.Response.ContentEncoding e HttpContext.Response.ContentType si traducono in:

// 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 da solo si traduce anche in:

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

HttpContext.Response.Output si traduce in:

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

HttpContext.Response.TransmitFile

La gestione di un file è descritta in Funzionalità di richiesta in ASP.NET Core.

HttpContext.Response.Headers

L'invio di intestazioni di risposta è complicato dal fatto che se si impostano le intestazioni dopo che è stato scritto qualcosa nel corpo della risposta, non verranno inviate.

La soluzione consiste nell'impostare un metodo di callback che verrà chiamato subito prima dell'avvio della scrittura nella risposta. Questa operazione viene eseguita al meglio all'inizio del Invoke metodo nel middleware. Si tratta di questo metodo di callback che imposta le intestazioni di risposta.

Il codice seguente imposta un metodo di callback denominato SetHeaders:

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

Il SetHeaders metodo di callback sarà simile al seguente:

// 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.Cookies

I cookie passano al browser in un'intestazione Set-responseCookie . Di conseguenza, l'invio di cookie richiede lo stesso callback usato per l'invio di intestazioni di risposta:

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

Il SetCookies metodo di callback sarà simile al seguente:

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 aggiuntive