从 .NET 7 中的 ASP.NET Core 迁移到 .NET 8

本文介绍如何将现有 ASP.NET Core 7.0 项目更新为 ASP.NET Core 8.0。

先决条件

更新 global.json 中的 .NET SDK 版本

如果你依靠 global.json 文件来定向特定的 .NET Core SDK 版本,请将 version 属性更新为已安装的 .NET 8.0 SDK 版本。 例如:

{
  "sdk": {
-    "version": "7.0.100"
+    "version": "8.0.100"
  }
}

更新目标框架

将项目文件的目标框架名字对象 (TFM) 更新为 net8.0

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
-    <TargetFramework>net7.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

</Project>

更新包引用

在项目文件中,将每个 Microsoft.AspNetCore.*Microsoft.EntityFrameworkCore.*Microsoft.Extensions.*System.Net.Http.Json 包引用的 Version 属性更新为 8.00 或更高版本。 例如:

<ItemGroup>
-   <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="7.0.12" />
-   <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.12" />
-   <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="7.0.0" />
-   <PackageReference Include="System.Net.Http.Json" Version="7.0.1" />
+   <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="8.0.0" />
+   <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.0" />
+   <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
+   <PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
</ItemGroup>

Blazor

涵盖以下迁移方案:

有关向 ASP.NET Core 应用添加 Blazor 支持的指导,请参阅将 ASP.NET Core Razor 组件集成到 ASP.NET Core 应用

更新 Blazor Server 应用

建议在 .NET 8 中使用 Blazor Web 应用,但支持 Blazor Server。 若要继续在 .NET 8 中使用 Blazor Server,请按照本文前三个部分中的指导进行操作:

为 Blazor Web 应用引入的新 Blazor 功能不适用于已更新为在 .NET 8 中运行的 Blazor Server 应用。 如果你想要采用新的 .NET 8 Blazor 功能,请按照以下任一部分中的指导进行操作:

采用所有 Blazor Web 应用约定

若要选择采用所有新的 Blazor Web 应用约定,建议执行以下过程:

  • 从 Blazor Web 应用项目模板创建新应用。 有关详细信息,请参阅用于 ASP.NET Core Blazor 的工具
  • 将应用的组件和代码移动到新的 Blazor Web 应用,进行修改以采用新功能。
  • 更新 Blazor Web 应用的布局和样式。

ASP.NET Core 8.0 的新增功能介绍了新的 .NET 8 功能。 从 .NET 6 或更早版本更新应用时,请参阅迁移和发行说明(新增功能文章),了解干预版本。

将 Blazor Server 应用转换为 Blazor Web 应用

.NET 8 支持 Blazor Server 应用,无需进行任何代码更改。 使用以下指导将 Blazor Server 应用转换为等效的 .NET 8 Blazor Web 应用,使所有新的 .NET 8 功能可用。

重要

本部分重点介绍将 .NET 7 Blazor Server 应用转换为 .NET 8 Blazor Web 应用所需的最小更改。 若要采用所有新的 Blazor Web 应用约定,请按照采用所有 Blazor Web 应用约定部分中的指导进行操作。

  1. 按照本文前三部分中的指导进行操作:

  2. App 组件 (App.razor) 的内容移动到添加到项目的根文件夹中的新 Routes 组件文件 (Routes.razor)。 将空 App.razor 文件保留在项目的根文件夹中的应用中。

  3. _Imports.razor 文件添加一个条目,使速记呈现模式可供应用使用:

    @using static Microsoft.AspNetCore.Components.Web.RenderMode
    
  4. _Host 页 (Pages/_Host.cshtml) 中的内容移动到空的 App.razor 文件中。 继续对 App 组件进行以下更改。

    注意

    在下面的示例中,项目的命名空间为 BlazorServerApp。 调整命名空间以匹配项目。

    从文件的顶部删除以下行:

    - @page "/"
    - @using Microsoft.AspNetCore.Components.Web
    - @namespace BlazorServerApp.Pages
    - @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
    

    将前面的行替换为用于注入 IHostEnvironment 实例的行:

    @inject IHostEnvironment Env
    

    <base> 标记的 href 中删除波形符 (~),并替换为应用的基本路径:

    - <base href="~/" />
    + <base href="/" />
    

    删除 HeadOutlet 组件的组件标记帮助程序,并将其替换为 HeadOutlet 组件。

    删除以下行:

    - <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
    

    将前面的行替换为以下内容:

    <HeadOutlet @rendermode="InteractiveServer" />
    

    删除 App 组件的组件标记帮助程序,并将其替换为 Routes 组件。

    删除以下行:

    - <component type="typeof(App)" render-mode="ServerPrerendered" />
    

    将前面的行替换为以下内容:

    <Routes @rendermode="InteractiveServer" />
    

    注意

    上述配置假定应用的组件采用交互式服务器呈现。 有关详细信息,包括如何采用静态服务器端呈现 (SSR),请参阅 ASP.NET 核心 Blazor 呈现模式

    删除错误 UI 的环境标记帮助程序,并将其替换为以下 Razor 标记。

    删除以下各行:

    - <environment include="Staging,Production">
    -     An error has occurred. This application may no longer respond until reloaded.
    - </environment>
    - <environment include="Development">
    -     An unhandled exception has occurred. See browser dev tools for details.
    - </environment>
    

    将前面的行替换为以下内容:

    @if (Env.IsDevelopment())
    {
        <text>
            An unhandled exception has occurred. See browser dev tools for details.
        </text>
    }
    else
    {
        <text>
            An error has occurred. This app may no longer respond until reloaded.
        </text>
    }
    

    将 Blazor 脚本从 blazor.server.js 更改为 blazor.web.js

    - <script src="_framework/blazor.server.js"></script>
    + <script src="_framework/blazor.web.js"></script>
    
  5. 删除 Pages/_Host.cshtml 文件,

  6. 更新Program.cs

    注意

    在下面的示例中,项目的命名空间为 BlazorServerApp。 调整命名空间以匹配项目。

    using 语句添加到项目命名空间的文件顶部:

    using BlazorServerApp;
    

    AddServerSideBlazor 替换为 AddRazorComponents 和对 AddInteractiveServerComponents 的链式调用。

    删除以下行:

    - builder.Services.AddServerSideBlazor();
    

    将前面的行替换为 Razor 组件和交互式服务器组件服务。 默认情况下,调用 AddRazorComponents 会添加防伪服务 (AddAntiforgery)。

    builder.Services.AddRazorComponents()
        .AddInteractiveServerComponents();
    

    删除以下行:

    - app.MapBlazorHub();
    

    将前面的行替换为对 MapRazorComponents 的调用,将 App 组件作为根组件类型提供,并向 AddInteractiveServerRenderMode 添加链式调用:

    app.MapRazorComponents<App>()
        .AddInteractiveServerRenderMode();
    

    删除以下行:

    - app.MapFallbackToPage("/_Host");
    

    调用 app.UseRouting 后,将 Antiforgery 中间件添加到请求处理管道。 如果有对 app.UseRoutingapp.UseEndpoints 的调用,则对 app.UseAntiforgery 的调用必须介于两者之间。 对 app.UseAntiforgery 的调用必须在对 app.UseAuthenticationapp.UseAuthorization 的调用后发出。 无需添加防伪服务 (builder.Services.AddAntiforgery()),因为 AddRazorComponents 会自动添加,这在前面已经介绍过。

    app.UseAntiforgery();
    
  7. 如果 Blazor Server 应用配置为禁用预呈现,则可以继续禁用更新的应用预呈现。 在 App 组件中,更改分配给 HeadOutletRoutes 组件的 @rendermodeRazor 指令属性的值。

    更改 HeadOutletRoutes 组件的 @rendermode 指令属性的值以禁用预呈现:

    - @rendermode="InteractiveServer"
    + @rendermode="new InteractiveServerRenderMode(prerender: false)"
    

    有关详细信息,请参阅 ASP.NET Core Blazor 呈现模式

更新 Blazor WebAssembly 应用

按照本文前三部分中的指导进行操作:

将托管的 Blazor WebAssembly 应用转换为 Blazor Web 应用

.NET 8 支持 Blazor WebAssembly 应用,无需进行任何代码更改。 使用以下指导将 ASP.NET Core 托管的 Blazor WebAssembly 应用转换为等效的 .NET 8 Blazor Web 应用,使所有新的 .NET 8 功能可用。

重要

本部分重点介绍将 .NET 7 ASP.NET Core 托管的 Blazor WebAssembly 应用转换为 .NET 8 Blazor Web 应用所需的最小更改。 若要采用所有新的 Blazor Web 应用约定,请按照采用所有 Blazor Web 应用约定部分中的指导进行操作。

  1. 按照本文前三部分中的指导进行操作:

    重要

    使用前面的指导更新解决方案的 .Client.Server.Shared 项目。

  2. .Client 项目文件 (.csproj) 中,添加以下 MS Build 属性:

    <NoDefaultLaunchSettingsFile>true</NoDefaultLaunchSettingsFile>
    <StaticWebAssetProjectMode>Default</StaticWebAssetProjectMode>
    

    另外,请从 .Client 项目文件中移除 Microsoft.AspNetCore.Components.WebAssembly.DevServer 包引用:

    - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer"... />
    
  3. 将文件内容从 .Client/wwwroot/index.html 文件移动到在 .Server 项目的根目录中创建的新 App 组件文件 (App.razor)。 移动文件的内容后,删除 index.html 文件。

    .Client 项目中的 App.razor 重命名为 Routes.razor

    Routes.razor 中,将 AppAssembly 属性的值更新为 typeof(Program).Assembly

  4. .Client 项目中,向 _Imports.razor 文件添加一个条目,使速记呈现模式可供应用使用:

    @using static Microsoft.AspNetCore.Components.Web.RenderMode
    

    创建 .Client 项目的 _Imports.razor 文件的副本,并将其添加到 .Server 项目中。

  5. App.razor 文件进行以下更改:

    将网站的默认网站标题 (<title>...</title>) 替换为 HeadOutlet 组件。 记下该网站标题供以后使用,并移除标题标签和标题:

    - <title>...</title>
    

    在移除标题的位置,放置一个分配交互式 WebAssembly 呈现模式(禁用预呈现)的 HeadOutlet 组件:

    <HeadOutlet @rendermode="new InteractiveWebAssemblyRenderMode(prerender: false)" />
    

    更改 CSS 样式捆绑包

    - <link href="{CLIENT PROJECT ASSEMBLY NAME}.styles.css" rel="stylesheet">
    + <link href="{SERVER PROJECT ASSEMBLY NAME}.styles.css" rel="stylesheet">
    

    前面代码中的占位符:

    • {CLIENT PROJECT ASSEMBLY NAME}:客户端项目程序集名称。 示例: BlazorSample.Client
    • {SERVER PROJECT ASSEMBLY NAME}:服务器项目程序集名称。 示例: BlazorSample.Server

    找到以下 <div>...</div> HTML 标记:

    - <div id="app">
    -     ...
    - </div>
    

    使用交互式 WebAssembly 呈现模式(禁用预呈现)将上述 <div>...</div> HTML 标记替换为 Routes 组件:

    <Routes @rendermode="new InteractiveWebAssemblyRenderMode(prerender: false)" />
    

    blazor.webassembly.js 脚本更新为 blazor.web.js

    - <script src="_framework/blazor.webassembly.js"></script>
    + <script src="_framework/blazor.web.js"></script>
    
  6. 打开 .Client 项目的布局文件 (.Client/Shared/MainLayout.razor),并添加具有网站默认标题({TITLE} 占位符)的 PageTitle 组件:

    <PageTitle>{TITLE}</PageTitle>
    

    注意

    其他布局文件也应接收具有默认网站标题的 PageTitle 组件。

    有关详细信息,请参阅在 ASP.NET Core Blazor 应用中控制头内容

  7. .Client/Program.cs 中删除以下行:

    - builder.RootComponents.Add<App>("#app");
    - builder.RootComponents.Add<HeadOutlet>("head::after");
    
  8. 更新.Server/Program.cs

    向项目添加 Razor 组件和交互式 WebAssembly 组件服务。 通过对 AddInteractiveWebAssemblyComponents 的链式调用来调用 AddRazorComponents。 默认情况下,调用 AddRazorComponents 会添加防伪服务 (AddAntiforgery)。

    builder.Services.AddRazorComponents()
        .AddInteractiveWebAssemblyComponents();
    

    Antiforgery 中间件添加到请求处理管道。

    放置以下代码:

    • 在对 app.UseRouting 的调用之后。
    • 如果有对 app.UseRoutingapp.UseEndpoints 的调用,则对 app.UseAntiforgery 的调用必须介于两者之间。
    • app.UseAntiforgery 的调用必须放在对 app.UseAuthorization 的调用(如果存在)之后。
    • 无需添加防伪服务 (builder.Services.AddAntiforgery()),因为 AddRazorComponents 会自动添加,这在前面已经介绍过。
    app.UseAntiforgery();
    

    删除以下行:

    - app.UseBlazorFrameworkFiles();
    

    删除以下行:

    - app.MapFallbackToFile("index.html");
    

    将前面的行替换为对 MapRazorComponents 的调用,将 App 组件作为根组件类型提供,并向 AddInteractiveWebAssemblyRenderModeAddAdditionalAssemblies 添加链式调用:

    app.MapRazorComponents<App>()
        .AddInteractiveWebAssemblyRenderMode()
        .AddAdditionalAssemblies(typeof({CLIENT APP NAMESPACE}._Imports).Assembly);
    

    在前面的示例中,{CLIENT APP NAMESPACE} 占位符是 .Client 项目的命名空间(例如,HostedBlazorApp.Client)。

  9. .Server 项目运行解决方案:

    对于 Visual Studio,确认在运行应用时在解决方案资源管理器中选择了 .Server 项目。

    如果使用 .NET CLI,请从 .Server 项目的文件夹运行项目。

更新服务和终结点选项配置

随着 .NET 8 中 Blazor Web 应用的发布,Blazor 服务和终结点选项配置已更新,引入了用于交互式组件服务和组件终结点配置的新 API。

更新的配置指南出现在以下位置:

使用 Yarp 路由解决方法删除 Blazor Server

如果之前遵循《在增量迁移中通过 Yarp 启用 ASP.NET Core Blazor Server 支持》中的指导使用 Yarp 将 Blazor Server 应用迁移到 .NET 6 或 .NET 7,可以反向执行按照本文指导所执行的解决步骤。 包含 Yarp 的 Blazor Server 的路由和深层链接在 .NET 8 中正常工作。

在布局组件中迁移CascadingValue组件

级联参数不会跨呈现模式边界传递数据,并且布局以静态方式呈现在其他交互式应用中。 因此,寻求在交互式呈现的组件中使用级联参数的应用将无法级联布局中的值。

迁移的两种方法为:

  • 建议)将状态作为根级级联值传递。 有关详细信息,请参阅根级级联值
  • 使用CascadingValue组件将路由器包装在Routes组件中,并使Routes组件以交互方式呈现。 有关示例,请参阅CascadingValue组件

有关详细信息,请参阅级联值/参数和呈现模式边界

迁移BlazorEnableCompression MSBuild 属性

对于禁用压缩和面向 .NET 7 或更早版本但使用 .NET 8 SDK 生成的Blazor WebAssembly应用,BlazorEnableCompression MSBuild 属性已更改为CompressionEnabled

<PropertyGroup>
-   <BlazorEnableCompression>false</BlazorEnableCompression>
+   <CompressionEnabled>false</CompressionEnabled>
</PropertyGroup>

使用 .NET CLI 发布命令时,请使用新属性:

dotnet publish -p:CompressionEnabled=false

有关详细信息,请参阅以下资源:

<CascadingAuthenticationState> 组件迁移到级联身份验证状态服务

在 .NET 7 或更早版本中,组件 CascadingAuthenticationState 包装在 UI 树的某些部分(例如围绕路由器周围 Blazor)以提供级联身份验证状态:

<CascadingAuthenticationState>
    <Router ...>
        ...
    </Router>
</CascadingAuthenticationState>

在 .NET 8 中,请勿使用 CascadingAuthenticationState 组件:

- <CascadingAuthenticationState>
      <Router ...>
          ...
      </Router>
- </CascadingAuthenticationState>

而是通过在 Program 文件中调用 AddCascadingAuthenticationState,将级联身份验证状态服务添加到服务集合:

builder.Services.AddCascadingAuthenticationState();

有关详细信息,请参阅以下资源:

有关 HTTP 缓存问题的新文章

我们添加了一篇新文章,讨论在跨主要版本升级 Blazor 应用时可能发生的一些常见 HTTP 缓存问题,以及如何解决 HTTP 缓存问题。

有关详细信息,请参阅在升级 ASP.NET Core Blazor 应用时避免 HTTP 缓存问题

关于使用静态服务器端呈现(静态 SSR)的类库的新文章

我们新增了一篇文章,其中讨论了使用静态服务器端呈现(静态 SSR)的 Razor 类库 (RCL) 中的组件库创作。

有关详细信息,请参阅使用静态服务器端呈现(静态 SSR)的 ASP.NET Core Razor 类库 (RCL)

从其他程序集中发现组件

从 Blazor Server 应用迁移到 Blazor Web 应用时,如果应用使用来自其他程序集(例如组件类库)的可路由组件,请访问 ASP.NET Core Blazor 路由和导航中的指南。

从查询字符串提供参数时删除 [Parameter] 属性

从查询字符串提供参数时,不再需要 [Parameter] 属性:

- [Parameter]
  [SupplyParameterFromQuery]

Blazor Server 脚本回退策略授权

在 .NET 7 中,Blazor Server 脚本 (blazor.server.js) 由静态文件中间件提供。 在 .NET 7 应用中,在调用授权中间件 (UseAuthorization) 之前将对静态文件中间件 (UseStaticFiles) 的调用放在请求处理管道中就足以向匿名用户提供 Blazor 脚本。

在 .NET 8 中,Blazor Server 脚本由其自己的终结点提供(使用终结点路由)。 此更改由以下资源引入:修复了 Bug - 将选项传递给 UseStaticFiles 时中断Blazor Server (dotnet/aspnetcore #45897)

请考虑多租户场景,其中:

  • 默认策略和回退策略的设置方式都相同。
  • 使用请求路径中的第一段(例如 tld.com/tenant-name/...)解析租户。
  • 向租户终结点发出的请求通过其他身份验证方案进行身份验证,该方案可向请求主体添加其他标识。
  • 回退授权策略要求通过其他标识检查声明。

针对 Blazor 脚本文件 (blazor.server.js) 的请求在 /_framework/blazor.server.js 中提供,此服务器在框架中进行了硬编码。 针对文件的请求未通过租户的附加身份验证方案进行身份验证,但仍会受到回退策略的质询,这会导致返回未经授权的结果

目前正在对 MapRazor 组件中的一个新框架功能评估此问题,该组件已被 FallbackPolicy RequireAuthenticatedUser (dotnet/aspnetcore 51836) 损坏,目前计划于 2024 年 11 月发布 .NET 9。 在此之前,可以使用以下三种方法中的任何一种方法解决此问题:

  • 不使用回退策略。 在 _Imports.razor 文件中应用 [Authorize] 属性,将其应用于应用的所有组件。 对于非 blazor 终结点,显式使用 [Authorize]RequireAuthorization

  • [AllowAnonymous] 添加到 Program 文件中的 /_framework/blazor.server.js 终结点:

    app.MapBlazorHub().Add(endpointBuilder =>
    {
        if (endpointBuilder is 
            RouteEndpointBuilder
            { 
                RoutePattern: { RawText: "/_framework/blazor.server.js" }
            })
        {
            endpointBuilder.Metadata.Add(new AllowAnonymousAttribute());
        }
    });
    
  • 注册自定义 AuthorizationHandler,以检查 HttpContext 是否允许 /_framework/blazor.server.js 文件通过。

Docker

更新 Docker 映像

对于使用 Docker 的应用,请更新 Dockerfile FROM 语句和脚本。 使用包含 ASP.NET Core 8.0 运行时的基础映像。 请注意 ASP.NET Core 7.0 和 8.0 之间的以下 docker pull 命令差异:

- docker pull mcr.microsoft.com/dotnet/aspnet:7.0
+ docker pull mcr.microsoft.com/dotnet/aspnet:8.0

更新 Docker 端口

.NET 容器映像中配置的默认 ASP.NET Core 端口已从端口 80 更新为 8080。

添加了新的 ASPNETCORE_HTTP_PORTS 环境变量,作为 ASPNETCORE_URLS 的更简单的替代方法。

有关详细信息,请参阅:

查看中断性变更

有关从 .NET Core .NET 7.0 到 8.0 的中断性变更,请参阅 .NET 8 中的中断性变更,其中包括 ASP.NET CoreEntity Framework Core 部分。