Atualizar do ASP.NET Framework para o ASP.NET Core

Por que atualizar para a última versão do .NET

O ASP.NET Core é a estrutura da Web moderna para .NET. Embora ASP.NET Core tenha muitas semelhanças com o ASP.NET no .NET Framework, é uma nova estrutura completamente reescrita. Os aplicativos do ASP.NET atualizados para o ASP.NET Core podem se beneficiar de um melhor desempenho e acesso aos recursos e funcionalidades mais recentes de desenvolvimento para a Web.

Abordagens de atualização do ASP.NET Framework

A maioria dos aplicativos não triviais do ASP.NET Framework deve considerar o uso da abordagem de atualização incremental. Para obter mais informações, confira Atualização do ASP.NET incremental para o ASP.NET Core.

Para aplicativos ASP.NET MVC e API Web, consulte Aprenda a atualizar do ASP.NET MVC e API Web para o ASP.NET Core MVC. Para aplicativos ASP.NET Framework Web Forms, consulte Aprenda a atualizar do ASP.NET Web Forms para o ASP.NET Core.

Padrões de aplicativos Web confiáveis

Consulte os Vídeo do YouTube e o artigoPadrão de Aplicativo Web Confiável para .NET para obter diretrizes sobre como criar um aplicativo de ASP.NET Core moderno, confiável, com desempenho, testável, econômico e escalonável, seja do zero ou refatorando um aplicativo existente.

Diferenças de decodificação de URI entre o ASP.NET e o ASP.NET Core

O ASP.NET Core tem as seguintes diferenças de decodificação de URI em relação ao ASP.NET Framework:

ASCII Encoded ASP.NET Core ASP.NET Framework
\ %5C \ /
/ %2F %2F /

Ao decodificar %2F no ASP.NET Core:

  • O caminho inteiro fica sem escape, exceto %2F, porque convertê-lo em / alteraria a estrutura do caminho. Ele não pode ser decodificado até que o caminho seja dividido em segmentos.

Para gerar o valor para HttpRequest.Url, use new Uri(this.AspNetCoreHttpRequest.GetEncodedUrl()); para evitar Uri interpretar os valores incorretamente.

Migrando segredos do usuário do ASP.NET Framework para o ASP.NET Core

Consulte este problema do GitHub.

Este artigo serve como um guia de referência para migração de aplicativos ASP.NET para o ASP.NET Core.

O Visual Studio tem ferramentas para ajudar a migrar aplicativos ASP.NET para ASP.NET Core. Para obter mais informações, confira Como migrar do ASP.NET para o ASP.NET Core no Visual Studio.

O Assistente de Atualização do .NET é uma ferramenta de linha de comando que pode ajudar a migrar do ASP.NET para o ASP.NET Core. Para obter mais informações, confira Visão geral do Assistente de Atualização do .NET e Atualizar um aplicativo ASP.NET MVC para .NET 6 com o .NET Upgrade Assistant.

Confira o livro eletrônico Como fazer a portabilidade de Bibliotecas para o .NET Core para obter um guia abrangente de portabilidade.

Pré-requisitos

SDK 2.2 ou posterior do .NET Core

Frameworks de destino

Projetos do ASP.NET Core oferecem aos desenvolvedores a flexibilidade de direcionamento para o .NET Core, .NET Framework ou ambos. Consulte Escolhendo entre o .NET Core e .NET Framework para aplicativos de servidor para determinar qual estrutura de destino é mais apropriada.

Ao usar o .NET Framework como destino, projetos precisam fazer referência a pacotes NuGet individuais.

Usar o .NET Core como destino permite que você elimine várias referências de pacote explícitas, graças ao metapacote do ASP.NET Core. Instale o metapacote Microsoft.AspNetCore.App em seu projeto:

<ItemGroup>
   <PackageReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

Quando o metapacote é usado, nenhum pacote referenciado no metapacote é implantado com o aplicativo. O repositório de runtime do .NET Core inclui esses ativos e eles são pré-compilados para melhorar o desempenho. Para saber mais, confira Metapacote Microsoft.AspNetCore.App para ASP.NET Core.

Diferenças de estrutura do projeto

O formato de arquivo .csproj foi simplificado no ASP.NET Core. Algumas alterações importantes incluem:

  • A inclusão explícita de arquivos não é necessária para que eles possam ser considerados como parte do projeto. Isso reduz o risco de conflitos de mesclagem XML ao trabalhar em grandes equipes.

  • Não há referências baseadas em GUID a outros projetos, o que melhora a legibilidade do arquivo.

  • O arquivo pode ser editado sem descarregá-lo no Visual Studio:

    Edit CSPROJ context menu option in Visual Studio 2017]

Substituição do arquivo Global.asax

O ASP.NET Core introduziu um novo mecanismo para inicializar um aplicativo. O ponto de entrada para aplicativos ASP.NET é o arquivo Global.asax. Tarefas como configuração de roteamento e registros de filtro e de área são tratadas no arquivo Global.asax.

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

Essa abordagem associa o aplicativo e o servidor no qual ele é implantado de uma forma que interfere na implementação. Em um esforço para desassociar, a OWIN foi introduzida para fornecer uma maneira mais limpa de usar várias estruturas juntas. A OWIN fornece um pipeline para adicionar somente os módulos necessários. O ambiente de hospedagem leva uma função Startup para configurar serviços e pipeline de solicitação do aplicativo. Startup registra um conjunto de middleware com o aplicativo. Para cada solicitação, o aplicativo chama cada um dos componentes de middleware com o ponteiro de cabeçalho de uma lista vinculada para um conjunto existente de manipuladores. Cada componente de middleware pode adicionar um ou mais manipuladores para a pipeline de tratamento de solicitação. Isso é feito retornando uma referência para o manipulador que é o novo cabeçalho da lista. Cada manipulador é responsável por se lembrar do próximo manipulador na lista e por invocá-lo. Com o ASP.NET Core, o ponto de entrada para um aplicativo é Startup e você não tem mais uma dependência de Global.asax. Ao usar a OWIN com o .NET Framework, use algo parecido com o seguinte como um pipeline:

using Owin;
using System.Web.Http;

namespace WebApi
{
    // Note: By default all requests go through this OWIN pipeline. Alternatively you can turn this off by adding an appSetting owin:AutomaticAppStartup with value “false”. 
    // With this turned off you can still have OWIN apps listening on specific routes by adding routes in global.asax file using MapOwinPath or MapOwinRoute extensions on RouteTable.Routes
    public class Startup
    {
        // Invoked once at startup to configure your application.
        public void Configuration(IAppBuilder builder)
        {
            HttpConfiguration config = new HttpConfiguration();
            config.Routes.MapHttpRoute("Default", "{controller}/{customerID}", new { controller = "Customer", customerID = RouteParameter.Optional });

            config.Formatters.XmlFormatter.UseXmlSerializer = true;
            config.Formatters.Remove(config.Formatters.JsonFormatter);
            // config.Formatters.JsonFormatter.UseDataContractJsonSerializer = true;

            builder.UseWebApi(config);
        }
    }
}

Isso configura as rotas padrão e usa XmlSerialization em Json por padrão. Adicione outro Middleware para este pipeline conforme necessário (carregamento de serviços, definições de configuração, arquivos estáticos, etc.).

O ASP.NET Core usa uma abordagem semelhante, mas não depende de OWIN para manipular a entrada. Em vez disso, isso é feito por meio do método de Program.csMain (semelhante a aplicativos de console) e Startup é carregado por lá.

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace WebApplication2
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }
}

Startup deve incluir um método Configure. Em Configure, adicione o middleware necessário ao pipeline. No exemplo a seguir (com base no modelo de site da Web padrão), os métodos de extensão configuram o pipeline com suporte para:

  • Páginas de erro
  • Segurança de Transporte Estrita de HTTP
  • Redirecionamento de HTTP para HTTPS
  • ASP.NET Core MVC
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseMvc();
}

O host e o aplicativo foram separados, o que fornece a flexibilidade de mover para uma plataforma diferente no futuro.

Observação

Para obter uma referência mais aprofundada sobre o Startup do ASP.NET Core e middleware, veja Startup no ASP.NET Core

Armazenar configurações

O ASP.NET dá suporte ao armazenamento de configurações. Essas configurações são usadas, por exemplo, para dar suporte ao ambiente no qual os aplicativos foram implantados. Uma prática comum era armazenar todos os pares chave-valor personalizados na seção <appSettings> do arquivo Web.config:

<appSettings>
  <add key="UserName" value="User" />
  <add key="Password" value="Password" />
</appSettings>

Aplicativos leem essas configurações usando a coleção ConfigurationManager.AppSettings no namespace System.Configuration:

string userName = System.Web.Configuration.ConfigurationManager.AppSettings["UserName"];
string password = System.Web.Configuration.ConfigurationManager.AppSettings["Password"];

O ASP.NET Core pode armazenar dados de configuração para o aplicativo em qualquer arquivo e carregá-los como parte da inicialização de middleware. O arquivo padrão usado nos modelos de projeto é appsettings.json:

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  },
  "AppConfiguration": {
    "UserName": "UserName",
    "Password": "Password"
  }
}

Carregar esse arquivo em uma instância de IConfiguration dentro de seu aplicativo é feito em Startup.cs:

public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

O aplicativo lê de Configuration para obter as configurações:

string userName = Configuration.GetSection("AppConfiguration")["UserName"];
string password = Configuration.GetSection("AppConfiguration")["Password"];

Existem extensões para essa abordagem para tornar o processo mais robusto, tais como o uso de DI (injeção de dependência) para carregar um serviço com esses valores. A abordagem de DI fornece um conjunto fortemente tipado de objetos de configuração.

// Assume AppConfiguration is a class representing a strongly-typed version of AppConfiguration section
services.Configure<AppConfiguration>(Configuration.GetSection("AppConfiguration"));

Observação

Para uma referência mais detalhada sobre configuração do ASP.NET Core, veja Configuração no ASP.NET Core.

Injeção de dependência nativa

Uma meta importante ao criar aplicativos escalonáveis e grandes é o acoplamento flexível de componentes e serviços. A injeção de dependência é uma técnica popular para conseguir isso e é também um componente nativo do ASP.NET Core.

Em aplicativos ASP.NET, os desenvolvedores contam com uma biblioteca de terceiros para implementar injeção de dependência. Um biblioteca desse tipo é a Unity, fornecida pelas Diretrizes da Microsoft.

Um exemplo de configuração da injeção de dependência com Unity é a implementação de IDependencyResolver, que encapsula uma UnityContainer:

using Microsoft.Practices.Unity;
using System;
using System.Collections.Generic;
using System.Web.Http.Dependencies;

public class UnityResolver : IDependencyResolver
{
    protected IUnityContainer container;

    public UnityResolver(IUnityContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        try
        {
            return container.Resolve(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        try
        {
            return container.ResolveAll(serviceType);
        }
        catch (ResolutionFailedException)
        {
            return new List<object>();
        }
    }

    public IDependencyScope BeginScope()
    {
        var child = container.CreateChildContainer();
        return new UnityResolver(child);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        container.Dispose();
    }
}

Crie uma instância de sua UnityContainer, registre seu serviço e defina o resolvedor de dependência de HttpConfiguration para a nova instância de UnityResolver para o contêiner:

public static void Register(HttpConfiguration config)
{
    var container = new UnityContainer();
    container.RegisterType<IProductRepository, ProductRepository>(new HierarchicalLifetimeManager());
    config.DependencyResolver = new UnityResolver(container);

    // Other Web API configuration not shown.
}

Injete IProductRepository quando necessário:

public class ProductsController : ApiController
{
    private IProductRepository _repository;

    public ProductsController(IProductRepository repository)  
    {
        _repository = repository;
    }

    // Other controller methods not shown.
}

Já que a injeção de dependência é parte do ASP.NET Core, você pode adicionar o serviço no método ConfigureServices de Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
    // Add application services.
    services.AddTransient<IProductRepository, ProductRepository>();
}

O repositório pode ser injetado em qualquer lugar, como ocorria com a Unity.

Observação

Para obter mais informações sobre injeção de dependência, confira Injeção de dependência.

Fornecer arquivos estáticos

Uma parte importante do desenvolvimento da Web é a capacidade de servir ativos estáticos, do lado do cliente. Os exemplos mais comuns de arquivos estáticos são HTML, CSS, Javascript e imagens. Esses arquivos precisam ser salvos no local de publicação do aplicativo (ou CDN) e referenciados para que eles possam ser carregados por uma solicitação. Esse processo foi alterado no ASP.NET Core.

No ASP.NET, arquivos estáticos são armazenados em vários diretórios e referenciados nas exibições.

No ASP.NET Core, arquivos estáticos são armazenados na "raiz da Web" (<raiz do conteúdo>/wwwroot), a menos que configurado de outra forma. Os arquivos são carregados no pipeline de solicitação invocando o método de extensão UseStaticFiles de Startup.Configure:

Observação

Se você usar o .NET Framework como destino, instale o pacote NuGet Microsoft.AspNetCore.StaticFiles.

Por exemplo, um ativo de imagem na pasta wwwroot/imagens está acessível para o navegador em um local como http://<app>/images/<imageFileName>.

Observação

Para obter uma referência mais aprofundada sobre como servir arquivos estáticos no ASP.NET Core, veja Arquivos estáticos.

cookies de vários valores

Não há suporte para cookies de vários valores no ASP.NET Core. Crie um cookie por valor.

Os cookies de autenticação não são compactados no ASP.NET Core

Por razões de segurança, os cookies de autenticação não são compactados no ASP.NET Core. Ao usar cookies de autenticação, os desenvolvedores devem minimizar o número de informações de declaração incluídas apenas para o necessário para suas necessidades.

Migração parcial do aplicativo

Uma abordagem para a migração parcial do aplicativo é criar um subaplicativo do IIS e mover apenas determinadas rotas do ASP.NET 4.x para o ASP.NET Core, preservando a estrutura de URL do aplicativo. Por exemplo, considere a estrutura de URL do aplicativo do arquivo applicationHost.config:

<sites>
    <site name="Default Web Site" id="1" serverAutoStart="true">
        <application path="/">
            <virtualDirectory path="/" physicalPath="D:\sites\MainSite\" />
        </application>
        <application path="/api" applicationPool="DefaultAppPool">
            <virtualDirectory path="/" physicalPath="D:\sites\netcoreapi" />
        </application>
        <bindings>
            <binding protocol="http" bindingInformation="*:80:" />
            <binding protocol="https" bindingInformation="*:443:" sslFlags="0" />
        </bindings>
    </site>
	...
</sites>

Estrutura de diretórios:

.
├── MainSite
│   ├── ...
│   └── Web.config
└── NetCoreApi
    ├── ...
    └── web.config

[ASSOCIAR] e Formatadores de entrada

Versões anteriores do ASP.NET usavam o atributo [Bind] para proteger contra ataques de excesso de postagem. Formatadores de entrada funcionam de forma diferente no ASP.NET Core. O atributo [Bind] não é mais projetado para evitar o excesso de postagem quando usado com formatadores de entrada para analisar JSON ou XML. Esses atributos afetam a vinculação de modelo quando a fonte de dados são dados de formulário postados com o tipo de conteúdo x-www-form-urlencoded.

Para aplicativos que postam informações JSON para controladores e usam formatadores de entrada JSON para analisar os dados, recomendamos substituir o atributo [Bind] por um modelo de exibição que corresponda às propriedades definidas pelo atributo [Bind].

Recursos adicionais