应用在线升级 · 使用手册

后台底部显示 应用 v1.0.0 · 检查更新。管理员点「检查更新 → 立即升级」,系统自动停服务、备份、替换文件、启动并健康检查,失败自动回滚,用户无需重新登录。

升级包是逐版本增量(1.0 → 1.1 → 1.2 逐版升),落后多个版本会自动依次走完每一步。

升级是扩展:不引用 EasyAdminBlazor.Upgrade,后台就没有「检查更新」入口、不注册 /health、发布时也不生成升级包。老项目行为完全不变。

一、安装扩展

方式 A:NuGet(推荐)

<ItemGroup>
  <PackageReference Include="EasyAdminBlazor.Upgrade" Version="2.4.0-preview.4" />
</ItemGroup>

一行搞定:扩展本体、升级引擎、发布钩子都在包里,不用写 Import,也不用把任何项目加进解决方案。

方式 B:源码引用(跟随框架源码开发)

<ItemGroup>
  <ProjectReference Include="..\..\yyq\EasyAdminBlazor\Extensions\EasyAdminBlazor.Upgrade\EasyAdminBlazor.Upgrade.Extension.csproj" />
</ItemGroup>

<!-- 放在 </Project> 之前 -->
<Import Project="..\..\yyq\EasyAdminBlazor\Extensions\EasyAdminBlazor.Upgrade\build\EasyAdminBlazor.Upgrade.targets" />

源码引用还需要把下面三个项目加进解决方案,否则 VS 会报「找不到项目信息」(命令行 dotnet build/publish 不受影响,会自动连带还原):

Extensions/EasyAdminBlazor.Upgrade/EasyAdminBlazor.Upgrade.Extension.csproj   扩展
Extensions/EasyAdminBlazor.Upgrade/Engine/EasyAdminBlazor.Upgrade.csproj      升级引擎
Extensions/EasyAdminBlazor.Upgrade/Updater/EasyAdminBlazor.Updater.csproj     升级程序 + 打包器

两种方式效果一样:发布时自动把升级程序放进 updater\,Release 配置下自动生成升级包。

二、启用升级

Program.cs:

var builder = WebApplication.CreateBuilder(args);

// Windows 服务形态需要;控制台 / 宝塔 可以不加
builder.Host.UseWindowsService();

builder.AddEasyAdminBlazor(new EasyAdminBlazorOptions
{
    // …你现有的配置不动…
})
// …你现有的扩展链…
.AddEasyAdminBlazorUpgrade();     // 启用升级(底部「检查更新」+ /health)

/health 由扩展自己挂载,宿主不需要写 app.MapGet("/health")。宿主原有的 app.UseEasyAdminBlazor(); 保留(它映射消息通知的 SignalR Hub)。

appsettings.json:

Windows 服务:

"Urls": "http://0.0.0.0:5099",
"Upgrade": {
  "Enabled": true,
  "Source": "Local",
  "ReleasesPath": "updates",
  "RestartMode": "WindowsService",
  "ServiceName": "你的服务名",
  "HealthCheckUrl": "http://127.0.0.1:5099"
}

Linux(宝塔):

"Urls": "http://127.0.0.1:5018",
"Upgrade": {
  "Enabled": true,
  "Source": "Local",
  "ReleasesPath": "updates",
  "RestartMode": "Direct",
  "HealthCheckUrl": "http://127.0.0.1:5018"
}

HealthCheckUrl 必须是本机能访问到的地址,端口与站点实际监听端口一致。 想让客户从发布站点点两下升级:Source 改成 Url,ReleaseIndexUrl 填 https://你的发布地址/releases/index.json(默认要求 HTTPS,内网 http 需加 "AllowInsecureHttp": true)。

三、出包

只改一处:宿主 csproj 的 <Version>。

dotnet publish -c Release -o publish

VS 右键发布(Release 配置)同样有效。发布完成后:

publish\
├── 程序文件 / wwwroot / updater\        ← updater\ 自动生成,别手工删
└── updates\
    ├── index.json
    ├── v1.0.0\  update.zip + release.json + files.json   ← 上一版(基线)
    └── v1.1.0\  update.zip + release.json + files.json   ← 本次增量包(约 1~2 MB)

用同一个发布目录连续发布,上一版基线才在,出的是增量包。想写更新说明,在项目根目录放 release-notes.txt,每行一条(# 开头是注释)。

四、Windows 部署(IIS 反向代理 + Kestrel 服务)

环境:安装 .NET 10 Runtime(需要 IIS 支持时装 ASP.NET Core Hosting Bundle),把 publish/ 内容放进应用目录(如 D:\app\EasyAdminBlazor)。

为什么 Windows 下只有在"Windows 服务"形态才能用在线升级?

在线升级要靠外部进程停掉应用、替换文件、再把它拉起来。IIS 进程内托管时应用跑在 w3wp.exe(应用程序池)里: 应用池回收/停止会连带杀掉它启动的子进程(升级程序),升级程序也没有权限把整个应用池停下来再启动; 而且一个应用池可能承载多个站点,停/起粒度对不上。因此框架检测到 IIS 进程内托管时会直接禁用升级入口 (页脚不显示「检查更新」、不注册 /health)。

所以 Windows 上要让应用以独立进程运行:注册成 Windows 服务(Kestrel 监听本地端口,见下),IIS 只做反向代理; 这样升级程序才能 sc stop/start 服务名 停掉并重新拉起应用。控制台运行(RestartMode: Direct)技术上也能升级, 但不适合生产(窗口关掉就没人管了)。

1. 注册为 Windows 服务

管理员命令行:

# 用系统自带的 sc
sc.exe create EasyAdminBlazor binPath= "D:\app\EasyAdminBlazor\你的应用.exe" start= auto
sc.exe start EasyAdminBlazor

# 或者 NSSM
nssm install EasyAdminBlazor "D:\app\EasyAdminBlazor\你的应用.exe"
nssm set EasyAdminBlazor AppDirectory "D:\app\EasyAdminBlazor"
nssm set EasyAdminBlazor Start SERVICE_AUTO_START
nssm start EasyAdminBlazor

验证:http://127.0.0.1:5099/health 应返回

{
    "status": "Healthy",
    "applicationVersion": "1.0.0",
    "frameworkVersion": "2.4.0-preview.4"
}

升级程序会执行:sc stop 服务名 → 等进程退出 → 备份 → 替换 → sc start 服务名 → 检查 /health 版本号。前提是服务账号有控制该服务的权限(sc create 默认的 LocalSystem 即可;受限账号需授予"启动/停止该服务"权限,并保证应用目录可写)。

2. IIS 反向代理配置

  1. 安装 ARR:IIS 管理器 → 服务器节点 → Application Request Routing Cache → Server Proxy Settings → 勾选 Enable proxy;
  2. 创建网站:IIS 管理器 → 网站 → 添加网站,物理路径指向一个空目录(用于放 web.config),应用程序池选“无托管代码”;
  3. 放 web.config:在网站根目录放下面这份 web.config(注意把 5099 改成你实际监听的端口):
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <rewrite>
      <rules>
        <rule name="ReverseProxyToKestrel" stopProcessing="true">
          <match url="(.*)" />
          <action type="Rewrite" url="http://127.0.0.1:5099/{R:1}" />
        </rule>
      </rules>
    </rewrite>

    <proxy preserveHostHeader="true" />

    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="104857600" />
      </requestFiltering>
    </security>
  </system.webServer>

  <system.web>
    <httpRuntime maxRequestLength="102400" executionTimeout="600" />
  </system.web>
</configuration>

说明:

  • preserveHostHeader="true" 保留原始 Host 头,否则 Blazor 的 URL 推导会出错;
  • maxAllowedContentLength 放宽到 100MB,覆盖文件上传场景;
  • 应用程序池必须是无托管代码模式,因为应用是 Kestrel 自托管的。

3. 应用池身份权限

IIS 应用池以 ApplicationPoolIdentity 运行,它需要能访问后端 Kestrel 监听的端口(127.0.0.1:5099)。因为是本机回环,通常无需额外配置。但如果站点报 502,检查 IIS 是否能访问该端口:

curl http://127.0.0.1:5099/health

如果这条命令在服务器上能通,IIS 的反代就没有问题。

五、Linux 宝塔部署(已实测可用,无需注册服务)

不需要注册 systemd 服务,也不要用宝塔的「进程守护」——直接用宝塔自带的 .NET 网站功能发布即可,升级程序会自己停/起进程。

  1. 发布:本机 dotnet publish -c Release -o publish,把 publish/ 整个传到 /www/wwwroot/your-app;
  2. 建站点:宝塔 → 网站 → 添加站点 → 选择 .NET 项目 / .NET 网站,项目目录指向 /www/wwwroot/your-app,启动文件 你的应用.dll,端口按宝塔给的(例如 5018);
  3. 确认端口:站点启动参数是 --urls http://localhost:5018,appsettings.json 里的 HealthCheckUrl 用同一个端口 http://127.0.0.1:5018;
  4. 配置:appsettings.json 用第二节里 Linux 那份(RestartMode: "Direct",端口 5018);
  5. 升级:把新版本的 updates/v1.1.0/ 和 updates/index.json 传到 <应用目录>/updates/,后台点「检查更新 → 立即升级」。

权限要点:应用目录要可写(升级要写 .upgrade/ 和程序文件),属主建议给 www。

六、把新版本发到服务器

服务器不需要重新部署程序,只把一个目录拷过去:

服务器 <应用目录>/updates/   ← 放入新版本目录(如 v1.1.0/)

然后管理员登录后台 →「检查更新」→「立即升级」,等页面自动刷新即可。升级期间服务停几十秒,页面自动重连,不用重新登录。

首次部署新服务器:把整个 publish/ 拷过去(已带 updates/v1.0.0/ 基线),之后每版只传增量目录(实测如果嫌基线版本占空间不放也行)。