基于 .NET Core Web API 的网站静态文件远程管理系统后端

前言

之前博主的博客是基于 Halo 的,但由于 Halo 是基于 Java ,对博主这种只买得起阿里云 2 核 2G 云服务器的贫困学生来说,压力还是太大。

所以博主就将 Halo 的博客重构为基于 Hexo 框架的纯静态博客,这样速度和服务器的压力都会有明显改善。

但是现在网上流传最多的 Hexo 自动部署方案都是基于 Github Action 的构建方法,而且基本上部署的平台都是网站空间(Vercel、Netlify 等网站托管平台),这就对于我们服务器用户不是那么的友好。

在博主经历过无数次:打开服务器后台 –> 登录 –> 找到网站根目录 –> 删除所有原有静态文件 –> 上传新网站的压缩包 –> 解压缩 –> 删除压缩包 的操作之后,我还是忍不住了,决定写一个一键上传压缩包,自动完成网站更新的项目。

最后,该项目为编程小白靠着自己的想法和 ChatGPT 完成,大佬勿喷🙏,项目也将持续完善。

项目准备

技术基础

  • C# 语言编程基础
  • ASP .NET Core Web API 基础了解

项目结构

  • 网页静态文件 ➡️ Web API ➡️ Nginx 反向代理 ➡️ 用户访问
  • 维护接口上传静态文件压缩包 ➡️ Web API (解压缩)➡️ 网页静态文件(删除、覆盖)

创建 .NET Core Web API 项目

文件目录:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MyApp/
├── MyApp.sln
├── MyApp/
│ ├── Properties/
│ │ └── LaunchSettings.json
│ ├── wwwroot/
│ │ ├── index.html
│ │ ├── 404.html
│ │ ├── css/
│ │ └── js/
│ ├── Controllers/
│ │ └── UploadController.cs
│ ├── appsettings.json
│ ├── appsettings.Development.cs
│ ├── Program.cs
│ └── ...

允许静态文件访问

在 Program.cs 文件中:

app.MapControllers(); 上方添加:

1
2
3
4
5
// 自动寻找index.html
app.UseDefaultFiles();

// 使用静态资源
app.UseStaticFiles();

添加 app.UseDefualtFiles(); 后,系统会自动寻找访问目录的 index.html 文件,例如访问 https://localhost:5144/ ,系统就会自动寻找 wwwroot (网站根目录)下的 index.html 文件。

添加 app.UseStaticFiles(); 后,系统会允许访问 wwwroot 里面的文件。

至此,Web API 就可以当作一个静态服务器,挂载静态资源。

解决未知页面自动跳转 404.html

在经过上述步骤之后,网站虽然能够正常访问,但还存在着一些问题。当我们访问一个不存在的页面的时候,网站就会直接返回 404 状态码,不会跳转到我们预先准备好的 404.html 文件。

解决方法:

在 Program.cs 文件中:

app.Run(); 上方添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 注意:不要用 MapFallbackToFile!
// 改用一个自定义的中间件来判断是否存在文件
app.Use(async (context, next) =>
{
await next(); // 让请求先经过前面的管道(包括静态文件、API)

// 如果满足以下条件,则返回 404.html
if (context.Response.StatusCode == 404 && // 状态码是404
!context.Request.Path.StartsWithSegments("/api") && // 不带 /api 路径
!Path.HasExtension(context.Request.Path.Value)) // 不含扩展名
{
context.Response.ContentType = "text/html";
await context.Response.SendFileAsync(Path.Combine(app.Environment.WebRootPath, "404.html"));
}
});

解析——两类“404”请求:

  1. 页面路径不存在(无扩展名)

    • 例如 /about、/blog/123

    • 对应的不是静态文件,而是前端路由或页面

    • 我们希望返回 自定义 404.html

    • 处理方式:

      1
      2
      3
      4
      if (!Path.HasExtension(...) && !StartsWithSegments("/api"))
      {
      返回 404.html
      }
  2. 静态资源不存在(有扩展名)

    • 例如 /css/missing.css、/js/app.js
    • 浏览器请求静态文件,如果不存在,默认 404
    • 如果你返回 404.html,浏览器会尝试把 HTML 当作 CSS/JS 加载 → 出错
    • 不处理,让浏览器返回原生 404 状态,更安全

为什么不对静态文件返回 404.html

假设用户访问 /css/missing.css,如果我们直接返回 404.html:

<link rel="stylesheet" href="/css/missing.css">

  • 服务器返回的是 HTML 内容(404.html)
  • 浏览器把 HTML 当作 CSS 解析 → 报错
  • 页面布局可能全部乱掉

同理,缺失 JS、图片、字体也会出错。

所以实践中,通常只对“页面请求”返回自定义 404.html,静态文件仍保留 404 状态码,让浏览器知道资源不存在。

通过 API 上传接收网站压缩包

在 Controllers 文件夹中,新建 UploadController.cs 文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System.IO.Compression;
using Microsoft.AspNetCore.Mvc;

namespace api_hexo_afwlhx.Controllers;

[ApiController]
[Route("api/[action]")]
public class UploadController : ControllerBase
{
private readonly IWebHostEnvironment _env;

public UploadController(IWebHostEnvironment env)
{
_env = env;
}

[HttpPost]
public async Task<IActionResult> Upload(IFormFile file, string key)
{
if (key != "your_key") return BadRequest(new { message = "错误key!" });

if (file == null || file.Length == 0)
return BadRequest("没有上传文件");

// 检查是否是 zip
if (!file.FileName.EndsWith(".zip", StringComparison.OrdinalIgnoreCase))
return BadRequest("请上传 zip 文件");

// 临时路径
var tempPath = Path.Combine(_env.ContentRootPath, "temp");
Directory.CreateDirectory(tempPath);

var zipPath = Path.Combine(tempPath, file.FileName);

// 保存 zip
using (var stream = new FileStream(zipPath, FileMode.Create))
{
await file.CopyToAsync(stream);
}

// 解压路径(wwwroot)
var wwwrootPath = Path.Combine(_env.ContentRootPath, "wwwroot");

// 清空旧文件(可选,视需求)
foreach (var dir in Directory.GetDirectories(wwwrootPath))
Directory.Delete(dir, true);
foreach (var f in Directory.GetFiles(wwwrootPath))
System.IO.File.Delete(f);

// 解压
ZipFile.ExtractToDirectory(zipPath, wwwrootPath);

// 删除 zip
System.IO.File.Delete(zipPath);

return Ok(new { message = "网站已更新成功" });
}
}

前端访问

至此,API 的搭建已完成。

接口地址:/api/Upload(可通过 /swagger/index.html 查看接口地址并测试接口)。