從 ASP.NET 移轉至 ASP.NET Core

作者:Isaac Levin

這篇文章可作為將 ASP.NET 應用程式移轉至 ASP.NET Core 的參考指南。

Visual Studio 有工具可協助將 ASP.NET 應用程式遷移至 ASP.NET Core。 如需詳細資訊,請參閱在 Visual Studio 中從 ASP.NET 移轉至 ASP.NET Core

.NET 升級小幫手是一個命令列工具,可協助您將 ASP.NET 移轉到 ASP.NET Core。 如需詳細資訊,請參閱 .NET 升級小幫手的概觀 (部分機器翻譯) 與使用 .NET 升級小幫手將 ASP.NET MVC 應用程式升級至 .NET 6 (部分機器翻譯)。

如需完整的移植指南,請參閱 Porting existing ASP.NET apps to .NET Core (將現有的 ASP.NET 應用程式移轉到 .NET Core) 電子書。

先決條件

.NET Core SDK 2.2 或更新版本

目標 Framework

ASP.NET Core 專案為開發人員提供了彈性,能以 .NET Core、.NET Framework 或兩者為目標。 請參閱針對伺服器應用程式在 .NET Core 和 .NET Framework 之間進行選擇,判斷哪些目標架構最適合。

以 .NET Framework 為目標時,專案需要參考個別的 NuGet 套件。

以 .NET Core 為目標,借助 ASP.NET Core 中繼套件,可讓您消除許多明確的套件參考。 在您的專案中安裝 Microsoft.AspNetCore.App 中繼套件:

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

使用中繼套件時,不使用應用程式部署中繼套件中參考的任何套件。 .NET Core 執行階段存放區包含這些資產,而且它們會先行編譯以改善效能。 如需詳細資訊,請參閱 ASP.NET Core 的 Microsoft.AspNetCore.App 中繼套件

專案結構差異

.csproj 檔案格式在 ASP.NET Core 中已簡化。 值得注意的變更包括:

  • 檔案不需要明確包含也會被視為專案的一部分。 在處理大型小組時,這會減少 XML 合併衝突的風險。

  • 其他專案沒有可改善檔案可讀性之以 GUID 為基礎的參考。

  • 您可以編輯檔案,卻不用在 Visual Studio 中卸載它:

    在 Visual Studio 2017 中編輯 CSPROJ 操作功能表選項

取代 Global.asax 檔案

ASP.NET Core 導入了啟動應用程式的新機制。 ASP.NET 應用程式的進入點是 Global.asax 檔案。 路由組態和篩選器和區域登錄等工作,會在 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);
    }
}

這個方法是以會影響到實作的方式,將應用程式和其部署所在的伺服器結合在一起。 為將它們分開,引進了 OWIN 以提供簡潔的方式,同時使用多個架構。 OWIN 提供的管線只新增所需的模組。 裝載環境採用 Startup 函式,設定服務和應用程式的要求管線。 Startup 向應用程式註冊一組中介軟體。 對於每項要求,應用程式會使用現有處理常式集合連結清單的標頭指標,呼叫每個中介軟體元件。 每個中介軟體元件都可以在要求處理管線新增一或多個處理常式。 這項作業是透過將參考傳回處理常式所完成,而此處理常式為清單的新標頭。 每個處理常式都負責記住和叫用清單中的下一個處理常式。 使用 ASP.NET Core,應用程式的進入點是 Startup,對 Global.asax 不會再有相依性。 使用 OWIN 和 .NET Framework 時,請將類似下列的項目當成管線使用:

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);
        }
    }
}

這會設定您的預設路由,並透過 JSON 將 XmlSerialization 設為預設值。 視需要在此管線新增其他中介軟體 (載入服務、組態設定、靜態檔案等等)。

ASP.NET Core 使用類似的方法,但不依賴 OWIN 處理項目。 相反地,這會透過 Program.csMain 方法 (類似主控台應用程式) 完成,而 Startup 也是透過該處載入。

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 必須包含 Configure 方法。 在 Configure 中將必要的中介軟體新增至管線。 在下列範例 (來自從預設的網站範本) 中,擴充方法會使用對下列項目的支援來設定管線:

  • 錯誤頁面
  • HTTP 嚴格的傳輸安全性
  • HTTP 重新導向 到 HTTPS
  • ASP.NET Core MVC
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseHsts();
    }

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

主機與應用程式已分離,這讓您未來可以彈性移至不同的平台。

注意

如需 ASP.NET Core 啟動與中介軟體更深入的參考,請參閱 ASP.NET Core 中的啟動

儲存組態

ASP.NET 支援儲存設定。 例如,這些設定是用來支援要部署應用程式的環境。 過去的常見做法是將所有自訂機碼值組儲存在 Web.config 檔案的 <appSettings> 區段中:

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

應用程式會讀取 System.Configuration 命名空間中這些使用 ConfigurationManager.AppSettings 集合的設定:

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

ASP.NET Core 可將應用程式的組態資料儲存在任何檔案中,將它們當成中介軟體啟動程序的一部分載入。 專案範本中所用的預設檔案是 appsettings.json

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

將此檔案載入應用程式內的 IConfiguration 執行個體,是在 Startup.cs 中完成:

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

public IConfiguration Configuration { get; }

應用程式讀取自 Configuration 以取得設定:

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

此方法有延伸模組可讓處理序更強固,例如使用相依性插入 (DI) 載入具有這些值的服務。 DI 方法提供強型別的組態物件集合。

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

注意

如需 ASP.NET Core 組態更深入的參考,請參閱 ASP.NET Core 中的組態

原生相依性插入

建置可延展的大型應用程式時,鬆散的元件和服務結合程度就是重要的目標。 相依性插入是達到此目標的常用技巧,它也是 ASP.NET Core 的原生元件。

在 ASP.NET 應用程式中,開發人員仰賴協力廠商程式庫來實作插入相依性。 Microsoft 模式和實務提供的 Unity 就是這樣的程式庫。

使用 Unity 設定相依性插入的範例,是實作包裝 UnityContainerIDependencyResolver

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();
    }
}

建立您 UnityContainer 的執行個體、註冊您的服務,以及為容器設定 UnityResolver 新執行個體的 HttpConfiguration 相依性解析程式:

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.
}

在需要的位置插入 IProductRepository

public class ProductsController : ApiController
{
    private IProductRepository _repository;

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

    // Other controller methods not shown.
}

因為相依性插入是 ASP.NET Core 的一部分,所以您可以在 Startup.csConfigureServices 方法中新增服務:

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

存放庫可插入任何位置,就像以前的 Unity 一樣。

注意

如需有關相依性插入的詳細資訊,請參閱相依性插入

提供靜態檔案

網頁程式開發很重要的一部分是能夠提供靜態的用戶端資產。 最常見的靜態檔案範例包括 HTML、CSS、Javascript 和影像。 這些檔案需要儲存在應用程式 (或 CDN) 的發佈位置供參考,以便要求可以載入它們。 此程序在 ASP.NET Core 中已變更。

在 ASP.NET 中,靜態檔案會儲存在不同目錄中,於檢視中提供參考。

在 ASP.NET Core 中,靜態檔案會儲存在「Web 根目錄」(<內容根目錄>/wwwroot) 中,除非另行設定。 從 Startup.Configure 叫用 UseStaticFiles 擴充方法,將檔案載入至要求管線:

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles();
}

注意

如以 .NET Framework 為目標,請安裝 NuGet 套件 Microsoft.AspNetCore.StaticFiles

例如,位在 http://<app>/images/<imageFileName> 等位置的瀏覽器可存取 wwwroot/images 資料夾中的影像資產。

注意

如需在 ASP.NET Core 中提供靜態檔案更深入的參考,請參閱靜態檔案

多重值 cookie

ASP.NET Core 中不支援多重值 cookie。 為每個值建立一個 cookie。

ASP.NET Core 中的驗證 cookie 不會經過壓縮。

基於安全性理由,ASP.NET Core 中的驗證 cookie 不會經過壓縮。 使用驗證 cookie 時,開發人員應該將所納入宣告資訊的數目最小化,只納入所需的宣告資訊。

部分應用程式移轉

進行部分應用程式移轉的其中一個方式是建立 IIS 子應用程式,並只將特定路由從 ASP.NET 4.x 移至 ASP.NET Core,同時保留應用程式的 URL 結構。 例如,考量來自 applicationHost.config 檔案的應用程式 URL 結構:

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

目錄結構:

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

[BIND] 與輸入格式器

舊版 ASP.NET 使用 [Bind] 屬性來抵禦過量張貼攻擊。 輸入格式器在 ASP.NET Core 中的運作方式截然不同。 搭配輸入格式器使用以剖析 JSON 或 XML 使用時,已不再指定 [Bind] 屬性以防止過量張貼。 當資料來源是使用 x-www-form-urlencoded 內容類型張貼的表單資料時,這些屬性會影響模型繫結。

針對將 JSON 資訊張貼到控制器並使用 JSON 輸入格式器來剖析資料的應用程式,我們建議您將 [Bind] 屬性 (Attribute) 取代為符合 [Bind] 屬性 (Attribute) 所定義屬性 (Property) 的檢視模型。

其他資源