Migración de controladores y módulos HTTP a middleware de ASP.NET CoreMigrate HTTP handlers and modules to ASP.NET Core middleware

En este artículo se muestra cómo migrar módulos y controladores http de ASP.net existentes de System. WebServer a ASP.net Core middleware.This article shows how to migrate existing ASP.NET HTTP modules and handlers from system.webserver to ASP.NET Core middleware.

Módulos y controladores revisadosModules and handlers revisited

Antes de continuar con ASP.NET Core middleware, vamos a resumir primero cómo funcionan los módulos y controladores HTTP:Before proceeding to ASP.NET Core middleware, let's first recap how HTTP modules and handlers work:

Controlador de módulos

Los controladores son:Handlers are:

  • Clases que implementan IHttpHandlerClasses that implement IHttpHandler

  • Se utiliza para controlar las solicitudes con un nombre de archivo o una extensión determinados, como . ReportUsed to handle requests with a given file name or extension, such as .report

  • Configurado en Web. configConfigured in Web.config

Los módulos son:Modules are:

  • Clases que implementan IHttpModuleClasses that implement IHttpModule

  • Se invoca para cada solicitudInvoked for every request

  • Capaz de cortocircuitar (detener el procesamiento de una solicitud)Able to short-circuit (stop further processing of a request)

  • Puede agregarse a la respuesta HTTP o crear su propiaAble to add to the HTTP response, or create their own

  • Configurado en Web. configConfigured in Web.config

El orden en que los módulos procesan las solicitudes entrantes viene determinado por:The order in which modules process incoming requests is determined by:

  1. Ciclo de vidade la aplicación, que es una serie de eventos desencadenados por ASP.net: BeginRequest, AuthenticateRequest, etc. Cada módulo puede crear un controlador para uno o más eventos.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. Para el mismo evento, el orden en el que están configurados en Web. config.For the same event, the order in which they're configured in Web.config.

Además de los módulos, puede agregar controladores para los eventos de ciclo de vida al archivo global.asax.CS .In addition to modules, you can add handlers for the life cycle events to your Global.asax.cs file. Estos controladores se ejecutan después de los controladores de los módulos configurados.These handlers run after the handlers in the configured modules.

De controladores y módulos a middlewareFrom handlers and modules to middleware

El middleware es más sencillo que los módulos y controladores HTTP:Middleware are simpler than HTTP modules and handlers:

  • Los módulos, controladores, global.asax.CS, Web. config (excepto para la configuración de IIS) y el ciclo de vida de la aplicación han desaparecidoModules, handlers, Global.asax.cs, Web.config (except for IIS configuration) and the application life cycle are gone

  • Los roles de los módulos y controladores se han tomado por middlewareThe roles of both modules and handlers have been taken over by middleware

  • El middleware se configura mediante código en lugar de en Web. config.Middleware are configured using code rather than in Web.config

  • La bifurcación de canalizaciones permite enviar solicitudes a middleware específico, en función de no solo la dirección URL sino también de encabezados de solicitud, cadenas de consulta, etc.Pipeline branching lets you send requests to specific middleware, based on not only the URL but also on request headers, query strings, etc.

El middleware es muy similar a los módulos:Middleware are very similar to modules:

El middleware y los módulos se procesan en un orden diferente:Middleware and modules are processed in a different order:

  • El orden de middleware se basa en el orden en que se insertan en la canalización de solicitudes, mientras que el orden de los módulos se basa principalmente en los eventos del ciclo de vida de la aplicación.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

  • El orden de las respuestas es el orden inverso al de las solicitudes, mientras que el orden de los módulos es el mismo para las solicitudes y las respuestas.Order of middleware for responses is the reverse from that for requests, while order of modules is the same for requests and responses

  • Consulte creación de una canalización de middleware con IApplicationBuilderSee Create a middleware pipeline with IApplicationBuilder

Software intermedio

Tenga en cuenta que en la imagen anterior, el middleware de autenticación cortocircuitó la solicitud.Note how in the image above, the authentication middleware short-circuited the request.

Migrar código de módulo a middlewareMigrating module code to middleware

Un módulo HTTP existente tendrá un aspecto similar al siguiente: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.
        }
    }
}

Como se muestra en la página de middleware , un middleware ASP.net Core es una clase que expone Invoke un método que HttpContext toma un y Taskdevuelve un.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. El nuevo middleware tendrá el siguiente aspecto: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>();
        }
    }
}

La plantilla de middleware anterior se tomó de la sección sobre escritura de middleware.The preceding middleware template was taken from the section on writing middleware.

La clase auxiliar MyMiddlewareExtensions facilita la configuración del middleware en la Startup clase.The MyMiddlewareExtensions helper class makes it easier to configure your middleware in your Startup class. El UseMyMiddleware método agrega la clase de middleware a la canalización de solicitudes.The UseMyMiddleware method adds your middleware class to the request pipeline. Los servicios que requiere el middleware se insertan en el constructor del middleware.Services required by the middleware get injected in the middleware's constructor.

El módulo puede finalizar una solicitud, por ejemplo, si el usuario no está autorizado: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;
    }
}

Para controlar esto, un middleware no Invoke llama a en el siguiente middleware de la canalización.A middleware handles this by not calling Invoke on the next middleware in the pipeline. Tenga en cuenta que esto no finaliza completamente la solicitud, ya que los middleware anteriores se seguirán invocando cuando la respuesta vuelva a su camino a través de la canalización.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.
}

Al migrar la funcionalidad del módulo al nuevo middleware, es posible que el código no se compile porque la HttpContext clase ha cambiado significativamente en 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. Más adelante, verá cómo migrar al nuevo ASP.net Core HttpContext.Later on, you'll see how to migrate to the new ASP.NET Core HttpContext.

Migración de la inserción de módulos en la canalización de solicitudesMigrating module insertion into the request pipeline

Normalmente, los módulos HTTP se agregan a la canalización de solicitudes mediante 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>

Convierta esto agregando el nuevo middleware a la canalización de Startup solicitudes en la clase: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?}");
    });
}

El punto exacto de la canalización en el que se inserta el middleware nuevo depende del evento que se administró comoBeginRequestmódulo EndRequest(,, etc.) y su orden en la lista de módulos de 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.

Como se indicó anteriormente, no hay ningún ciclo de vida de la aplicación en ASP.NET Core y el orden en el que el middleware procesa las respuestas difiere del orden utilizado por los módulos.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. Esto podría hacer que su decisión de ordenación sea más desafiante.This could make your ordering decision more challenging.

Si la ordenación se convierte en un problema, puede dividir el módulo en varios componentes de middleware que se pueden ordenar de forma independiente.If ordering becomes a problem, you could split your module into multiple middleware components that can be ordered independently.

Migrar el código del controlador a middlewareMigrating handler code to middleware

Un controlador HTTP tiene un aspecto similar al siguiente: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";
        }
    }
}

En el proyecto de ASP.NET Core, se traducirá a un middleware similar a este: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>();
        }
    }
}

Este middleware es muy similar al middleware correspondiente a los módulos.This middleware is very similar to the middleware corresponding to modules. La única diferencia real es que no hay ninguna llamada a _next.Invoke(context).The only real difference is that here there's no call to _next.Invoke(context). Esto tiene sentido, porque el controlador está al final de la canalización de solicitudes, por lo que no habrá ningún middleware siguiente que invocar.That makes sense, because the handler is at the end of the request pipeline, so there will be no next middleware to invoke.

Migrar la inserción del controlador a la canalización de solicitudesMigrating handler insertion into the request pipeline

La configuración de un controlador HTTP se realiza en Web. config y tiene un aspecto similar al siguiente: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>

Puede convertir esto agregando el nuevo middleware del controlador a la canalización de solicitudes Startup en la clase, de forma similar al middleware convertido desde módulos.You could convert this by adding your new handler middleware to the request pipeline in your Startup class, similar to middleware converted from modules. El problema con este enfoque es que enviaría todas las solicitudes al nuevo middleware del controlador.The problem with that approach is that it would send all requests to your new handler middleware. Sin embargo, solo desea que las solicitudes con una extensión determinada lleguen a su middleware.However, you only want requests with a given extension to reach your middleware. Esto le proporcionaría la misma funcionalidad que tenía con el controlador HTTP.That would give you the same functionality you had with your HTTP handler.

Una solución consiste en bifurcar la canalización para las solicitudes con una extensión determinada MapWhen , mediante el método de extensión.One solution is to branch the pipeline for requests with a given extension, using the MapWhen extension method. Esto se realiza en el mismo Configure método en el que se agrega el otro 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?}");
    });
}

MapWhentoma estos parámetros:MapWhen takes these parameters:

  1. Una expresión lambda que toma HttpContext y devuelve true si la solicitud debe desplazarse por la rama.A lambda that takes the HttpContext and returns true if the request should go down the branch. Esto significa que puede crear una bifurcación de solicitudes no solo basándose en su extensión, sino también en encabezados de solicitud, parámetros de cadena de consulta, etc.This means you can branch requests not just based on their extension, but also on request headers, query string parameters, etc.

  2. Una expresión lambda que toma IApplicationBuilder un y agrega todo el middleware para la rama.A lambda that takes an IApplicationBuilder and adds all the middleware for the branch. Esto significa que puede Agregar middleware adicional a la rama delante del middleware del controlador.This means you can add additional middleware to the branch in front of your handler middleware.

Middleware que se agrega a la canalización antes de que se invoque la rama en todas las solicitudes; la rama no tendrá ningún impacto en ellos.Middleware added to the pipeline before the branch will be invoked on all requests; the branch will have no impact on them.

Cargar opciones de middleware mediante el patrón de opcionesLoading middleware options using the options pattern

Algunos módulos y controladores tienen opciones de configuración que se almacenan en Web. config. Sin embargo, en ASP.NET Core se usa un nuevo modelo de configuración en lugar de 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.

El nuevo sistema de configuración le proporciona estas opciones para solucionar este problemas:The new configuration system gives you these options to solve this:

  1. Cree una clase que contenga las opciones de middleware, por ejemplo:Create a class to hold your middleware options, for example:

    public class MyMiddlewareOptions
    {
        public string Param1 { get; set; }
        public string Param2 { get; set; }
    }
    
  2. Almacenar los valores de opciónStore the option values

    El sistema de configuración permite almacenar valores de opciones en cualquier lugar que desee.The configuration system allows you to store option values anywhere you want. Sin embargo, la mayoría de los sitios usan appSettings. JSON, por lo que tomaremos este enfoque:However, most sites use appsettings.json, so we'll take that approach:

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

    MyMiddlewareOptionsSection aquí es un nombre de sección.MyMiddlewareOptionsSection here is a section name. No tiene que ser el mismo que el nombre de la clase de opciones.It doesn't have to be the same as the name of your options class.

  3. Asociar los valores de opción a la clase OptionsAssociate the option values with the options class

    El patrón de opciones usa el marco de inserción de dependencias de ASP.net Core para asociar MyMiddlewareOptionsel tipo de MyMiddlewareOptions opciones (como) a un objeto que tiene las opciones reales.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.

    Actualice su Startup clase:Update your Startup class:

    1. Si utiliza appSettings. JSON, agréguelo al generador de configuración en el Startup constructor: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. Configurar el servicio de opciones: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. Asocie las opciones a la clase de opciones: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. Inserte las opciones en el constructor de middleware.Inject the options into your middleware constructor. Esto es similar a insertar opciones en un controlador.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
        }
    }
    

    El método de extensión UseMiddleware que agrega el middleware a IApplicationBuilder se encarga de la inserción de dependencias.The UseMiddleware extension method that adds your middleware to the IApplicationBuilder takes care of dependency injection.

    Esto no se limita IOptions a los objetos.This isn't limited to IOptions objects. Cualquier otro objeto que requiera el middleware puede insertarse de esta manera.Any other object that your middleware requires can be injected this way.

Carga de opciones de middleware mediante inyección directaLoading middleware options through direct injection

El patrón de opciones tiene la ventaja de que crea un acoplamiento flexible entre los valores de opciones y sus consumidores.The options pattern has the advantage that it creates loose coupling between options values and their consumers. Una vez que haya asociado una clase de opciones con los valores de las opciones reales, cualquier otra clase puede obtener acceso a las opciones a través del marco de inserción de dependencias.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. No es necesario pasar valores de opciones.There's no need to pass around options values.

Esto se interrumpe si desea utilizar el mismo middleware dos veces, con distintas opciones.This breaks down though if you want to use the same middleware twice, with different options. Por ejemplo, un middleware de autorización usado en distintas bifurcaciones, lo que permite diferentes roles.For example an authorization middleware used in different branches allowing different roles. No se pueden asociar dos objetos de opciones diferentes a una clase de opciones.You can't associate two different options objects with the one options class.

La solución consiste en obtener los objetos de opciones con los valores de las opciones Startup reales de la clase y pasarlos directamente a cada instancia 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. Agregar una segunda clave a appSettings. JSONAdd a second key to appsettings.json

    Para agregar un segundo conjunto de opciones al archivo appSettings. JSON , use una nueva clave para identificarla de forma única: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. Recupera los valores de opciones y los pasa a middleware.Retrieve options values and pass them to middleware. El Use... método de extensión (que agrega el middleware a la canalización) es un lugar lógico para pasar los valores de opción: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. Habilite el middleware para tomar un parámetro de opciones.Enable middleware to take an options parameter. Proporcione una sobrecarga del método Use... de extensión (que toma el parámetro Options y lo pasa UseMiddlewarea).Provide an overload of the Use... extension method (that takes the options parameter and passes it to UseMiddleware). Cuando UseMiddleware se llama a con parámetros, pasa los parámetros al constructor de middleware cuando crea una instancia del objeto 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));
        }
    }
    

    Observe cómo incluye el objeto de opciones en un OptionsWrapper objeto.Note how this wraps the options object in an OptionsWrapper object. Implementa IOptions, según lo esperado por el constructor de middleware.This implements IOptions, as expected by the middleware constructor.

Migración al nuevo HttpContextMigrating to the new HttpContext

Anteriormente, vio que el Invoke método en el middleware toma un parámetro de tipo HttpContext:You saw earlier that the Invoke method in your middleware takes a parameter of type HttpContext:

public async Task Invoke(HttpContext context)

HttpContextha cambiado significativamente en ASP.NET Core.HttpContext has significantly changed in ASP.NET Core. En esta sección se muestra cómo trasladar las propiedades usadas con más frecuencia de System. Web. HttpContext al nuevo 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 se convierte en:HttpContext.Items translates to:

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

IDENTIFICADOR de solicitud único (no homólogo System. Web. HttpContext)Unique request ID (no System.Web.HttpContext counterpart)

Proporciona un identificador único para cada solicitud.Gives you a unique id for each request. Es muy útil incluir en los registros.Very useful to include in your logs.

string requestId = httpContext.TraceIdentifier;

HttpContext.RequestHttpContext.Request

HttpContext. request. HttpMethod se convierte en:HttpContext.Request.HttpMethod translates to:

string httpMethod = httpContext.Request.Method;

HttpContext. request. QueryString se convierte en: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 y HttpContext. request. RawUrl se traducen a:HttpContext.Request.Url and HttpContext.Request.RawUrl translate to:

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

HttpContext. request. IsSecureConnection se convierte en:HttpContext.Request.IsSecureConnection translates to:

var isSecureConnection = httpContext.Request.IsHttps;

HttpContext. request. UserHostAddress se convierte en:HttpContext.Request.UserHostAddress translates to:

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

HttpContext. request. cookies se convierte en: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 se convierte en:HttpContext.Request.RequestContext.RouteData translates to:

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

HttpContext. request. Headers se convierte en: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 se convierte en:HttpContext.Request.UserAgent translates to:

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

HttpContext. request. UrlReferrer se convierte en:HttpContext.Request.UrlReferrer translates to:

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

HttpContext. request. ContentType se convierte en: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 se convierte en: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"];
}

Advertencia

Lea los valores del formulario solo si el subtipo de contenido es 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 se convierte en:HttpContext.Request.InputStream translates to:

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

Advertencia

Use este código únicamente en un middleware de tipo de controlador, al final de una canalización.Use this code only in a handler type middleware, at the end of a pipeline.

Puede leer el cuerpo sin procesar como se muestra anteriormente solo una vez por solicitud.You can read the raw body as shown above only once per request. El middleware que intenta leer el cuerpo después de la primera lectura leerá un cuerpo vacío.Middleware trying to read the body after the first read will read an empty body.

Esto no se aplica a la lectura de un formulario como se mostró anteriormente, ya que se realiza desde un búfer.This doesn't apply to reading a form as shown earlier, because that's done from a buffer.

HttpContext.ResponseHttpContext.Response

HttpContext. Response. status y HttpContext. Response. StatusDescription se traducen a:HttpContext.Response.Status and HttpContext.Response.StatusDescription translate to:

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

HttpContext. Response. ContentEncoding y HttpContext. Response. ContentType se traducen a: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 por sí mismo también se convierte en:HttpContext.Response.ContentType on its own also translates to:

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

HttpContext. Response. Output se convierte en:HttpContext.Response.Output translates to:

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

HttpContext.Response.TransmitFileHttpContext.Response.TransmitFile

El servicio de un archivo se describe aquí.Serving up a file is discussed here.

HttpContext.Response.HeadersHttpContext.Response.Headers

El envío de encabezados de respuesta es complicado por el hecho de que, si los establece después de haber escrito algo en el cuerpo de la respuesta, no se enviarán.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 solución consiste en establecer un método de devolución de llamada que se llamará justo antes de que se inicie la respuesta.The solution is to set a callback method that will be called right before writing to the response starts. Lo mejor es hacerlo al principio del Invoke método en el middleware.This is best done at the start of the Invoke method in your middleware. Es este método de devolución de llamada que establece los encabezados de respuesta.It's this callback method that sets your response headers.

El código siguiente establece un método de devolución SetHeadersde llamada llamado:The following code sets a callback method called SetHeaders:

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

El SetHeaders método de devolución de llamada tendría el siguiente aspecto: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

Las cookies viajan al explorador en un encabezado de respuesta Set-Cookie .Cookies travel to the browser in a Set-Cookie response header. Como resultado, el envío de cookies requiere la misma devolución de llamada que se usa para enviar encabezados de respuesta: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);

El SetCookies método de devolución de llamada sería similar al siguiente: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); 
}

Recursos adicionalesAdditional resources