Partilhar via


Adaptadores System.Web

O principal caso de uso dos adaptadores no repositório dotnet/systemweb-adapters é ajudar os desenvolvedores que confiaram em System.Web tipos nas suas bibliotecas de classe ao desejarem migrar para o ASP.NET Core.

Um recurso importante dos adaptadores é que os adaptadores permitem que a biblioteca seja usada a partir de projetos ASP.NET Framework e ASP.NET Core. A atualização de vários aplicativos do ASP.NET Framework para ASP.NET Core geralmente envolve estados intermediários onde nem todos os aplicativos foram totalmente atualizados. Usando os System.Web adaptadores, a biblioteca pode ser usada tanto de chamadores do ASP.NET Core quanto de chamadores do ASP.NET Framework que não foram atualizados.

Vamos dar uma olhada em um exemplo usando os adaptadores que estão sendo movidos do .NET Framework para o ASP.NET Core.

Pacotes

  • Microsoft.AspNetCore.SystemWebAdapters: Este pacote é usado em bibliotecas de suporte e fornece as APIs System.Web das quais você pode ter dependido, como HttpContext e outras. Este pacote destina-se ao .NET Standard 2.0, .NET Framework 4.5+ e .NET 5+.
  • Microsoft.AspNetCore.SystemWebAdapters.FrameworkServices: Este pacote destina-se apenas ao .NET Framework e destina-se a fornecer serviços a aplicações do ASP.NET Framework que possam necessitar de fornecer migrações incrementais. Geralmente, não se espera que isso seja referenciado a partir de bibliotecas, mas sim dos próprios aplicativos.
  • Microsoft.AspNetCore.SystemWebAdapters.CoreServices: Este pacote destina-se apenas ao .NET 6+ e destina-se a fornecer serviços a aplicações ASP.NET Core para configurar o comportamento de System.Web APIs, bem como optar por quaisquer comportamentos para migração incremental. Geralmente, não se espera que isso seja referenciado a partir de bibliotecas, mas sim dos próprios aplicativos.
  • Microsoft.AspNetCore.SystemWebAdapters.Abstractions: Este pacote é um pacote de suporte que fornece abstrações para serviços usados pelo aplicativo ASP.NET Core e ASP.NET Framework, como serialização de estado de sessão.

Convertendo para System.Web.HttpContext

Para converter entre as duas representações de HttpContext, você pode fazer o seguinte:

HttpContext Para HttpContext:

  • Conversão implícita
  • HttpContext.AsSystemWeb()

Para HttpContext a HttpContext

  • Conversão implícita
  • HttpContext.AsAspNetCore()

Ambos os métodos usarão uma representação em cache de HttpContext durante a duração de uma solicitação. Isso permite regravações direcionadas para HttpContext conforme necessário.

Exemplo

ASP.NET Framework

Considere um controlador que faça algo como:

public class SomeController : Controller
{
  public ActionResult Index()
  {
    SomeOtherClass.SomeMethod(HttpContext.Current);
  }
}

que então tem lógica em uma assembly separada passando HttpContext até que, finalmente, algum método interno executa alguma lógica sobre ela, como:

public class Class2
{
  public bool PerformSomeCheck(HttpContext context)
  {
    return context.Request.Headers["SomeHeader"] == "ExpectedValue";
  }
}

ASP.NET Core

Para executar a lógica acima no ASP.NET Core, um desenvolvedor precisará adicionar o Microsoft.AspNetCore.SystemWebAdapters pacote, que permitirá que os projetos funcionem em ambas as plataformas.

As bibliotecas precisariam ser atualizadas para entender os adaptadores, mas será tão simples quanto adicionar o pacote e recompilar. Se estas forem as únicas dependências que um sistema tem em System.Web.dll, então as bibliotecas poderão direcionar o .NET Standard 2.0 para facilitar um processo de construção mais simples durante a migração.

O controlador no ASP.NET Core agora terá esta aparência:

public class SomeController : Controller
{
  [Route("/")]
  public IActionResult Index()
  {
    SomeOtherClass.SomeMethod(HttpContext);
  }
}

Observe que, como há a propriedade HttpContext, eles podem passar por ela, mas geralmente parece igual. Usando conversões implícitas, o HttpContext pode ser convertido no adaptador que pode ser passado através dos níveis utilizando o código da mesma maneira.

Testes Unitários

Ao testar unidades de código que utilizam os adaptadores System.Web, há algumas considerações especiais a ter em mente.

Na maioria dos casos, não há necessidade de configurar componentes adicionais para executar testes. Mas se o componente que está sendo testado usa HttpRuntime, pode ser necessário iniciar o SystemWebAdapters serviço, como mostrado no exemplo a seguir:

namespace TestProject1;

/// <summary>
/// This demonstrates an xUnit feature that ensures all tests
/// in classes marked with this collection are run sequentially.
/// </summary>
[CollectionDefinition(nameof(SystemWebAdaptersHostedTests),
    DisableParallelization = true)]
public class SystemWebAdaptersHostedTests
{
}
[Collection(nameof(SystemWebAdaptersHostedTests))]
public class RuntimeTests
{
    /// <summary>
    /// This method starts up a host in the background that
    /// makes it possible to initialize <see cref="HttpRuntime"/>
    /// and <see cref="HostingEnvironment"/> with values needed 
    /// for testing with the <paramref name="configure"/> option.
    /// </summary>
    /// <param name="configure">
    /// Configuration for the hosting and runtime options.
    /// </param>
    public static async Task<IDisposable> EnableRuntimeAsync(
        Action<SystemWebAdaptersOptions>? configure = null,
        CancellationToken token = default)
        => await new HostBuilder()
           .ConfigureWebHost(webBuilder =>
           {
               webBuilder
                   .UseTestServer()
                   .ConfigureServices(services =>
                   {
                       services.AddSystemWebAdapters();
                       if (configure is not null)
                       {
                           services.AddOptions
                               <SystemWebAdaptersOptions>()
                               .Configure(configure);
                       }
                   })
                   .Configure(app =>
                   {
                       // No need to configure pipeline for tests
                   });
           })
           .StartAsync(token);
    [Fact]
    public async Task RuntimeEnabled()
    {
        using (await EnableRuntimeAsync(options =>
            options.AppDomainAppPath = "path"))
        {
            Assert.True(HostingEnvironment.IsHosted);
            Assert.Equal("path", HttpRuntime.AppDomainAppPath);
        }
        Assert.False(HostingEnvironment.IsHosted);
    }
}

Os testes devem ser executados em sequência e não em paralelo. O exemplo anterior ilustra como conseguir isso definindo a opção do DisableParallelization XUnit como true. Essa configuração desabilita a execução paralela para uma coleção de teste específica, garantindo que os testes dentro dessa coleção sejam executados um após o outro, sem simultaneidade.