Maui Blazor 中文社区 QQ群:645660665

MAUI 嵌入式 Web 架构实战(四) 静态文件托管与前端框架整合

MAUI 嵌入式 Web 架构实战(四)

PicoServer 静态文件托管与 Vue / React 前端整合

源码地址:
https://github.com/densen2014/MauiPicoAdmin


在前几篇文章中,我们已经完成了 PicoServer 在 MAUI 中的核心能力:

  • .NET MAUI 应用中嵌入本地 HTTP 服务
  • 使用 PicoServer 构建本地 REST API
  • 实现 Controller / Service 分层架构

此时我们的应用已经具备:

本地 API Server

但一个完整的应用系统通常包含两部分:

后端 API
前端界面

如果只有 API,仍然无法构建完整应用。

因此在本篇文章中,我们将继续升级 PicoServer,使其具备:

静态文件托管能力

也就是让 MAUI 应用能够直接运行 Web 前端,例如:

HTML
Vue
React
Admin Dashboard

最终实现一个完整的 本地 Web Admin 系统

整体架构如下:

浏览器 / WebView
        │
        ▼
 http://localhost:8090
        │
        ▼
     Web Admin
        │
   fetch API
        │
        ▼
    PicoServer
        │
        ▼
    MAUI 业务逻辑

一、什么是静态文件托管

静态文件托管指的是服务器直接返回文件,例如:

URL 返回文件
/ index.html
/css/app.css CSS 文件
/js/app.js JavaScript 文件

例如访问:

http://127.0.0.1:8090/index.html

服务器会读取:

wwwroot/index.html

然后返回给浏览器。

这种模式与 传统 Web Server(Nginx / Apache / ASP.NET)完全一致


二、创建静态资源目录

首先在 MAUI 项目中创建目录:

Resources\Raw\wwwroot

推荐结构:

wwwroot
 ├─ index.html
 ├─ css
 │   └─ app.css
 ├─ js
 │   └─ app.js
 └─ images

这个目录名称在 Web 框架中非常常见,例如:

ASP.NET Core
Spring Boot
Node.js

都使用 Resources\Raw\wwwroot 作为静态目录。


三、创建测试页面

创建一个简单页面:

index.html

<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8">
<title>PicoServer Admin</title>
</head>

<body>

<h1>PicoServer Web Admin</h1>

<button onclick="loadTime()">获取服务器时间</button>

<p id="result"></p>

<script>

async function loadTime()
{
    let res = await fetch("/api/time");
    let data = await res.json();

    document.getElementById("result").innerText = data.data.time;
}

</script>

</body>
</html>

这个页面会调用之前实现的 API:

/api/time

四、实现静态文件读取

静态文件(index.html)放到工程 Resources\Raw\wwwroot 目录

需要做以下步骤才能实现文件系统

  1. 定义路径.
public readonly string wwwrootPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");
  1. 编辑项目文件,实现每次 Debug/Build 前自动生成 wwwroot/filelist.txt,内容为所有 wwwroot 下的文件相对路径。无需手动维护清单,App 启动时可自动释放所有包内静态文件。

image

	<!--每次 Debug 前自动生成 wwwroot/filelist.txt,内容为 wwwroot 目录下所有文件的相对路径。-->
	
	<Target Name="GenerateWwwrootFileList" BeforeTargets="Build">
    <ItemGroup>
      <WwwrootFiles Include="Resources\Raw\wwwroot\**\*" />
    </ItemGroup>
    <WriteLinesToFile
      File="Resources\Raw\wwwroot\filelist.txt"
      Lines="@(WwwrootFiles->'%(RecursiveDir)%(Filename)%(Extension)')"
      Overwrite="true" />

五、释放资源到可写文件夹

由于我们用的是MAUI,除了windows平台能直接访问,其他的平台需要手动释放资源到可写文件夹.

添加如下方法: App 启动时可自动释放所有包内静态文件

    public PicoServerHost()
    {
        EnsureWwwrootExistsAsync().Wait(); //添加这行
        RegisterRoutes();
        api.StartServer();
    }

    /// <summary>
    /// 每次 Debug/Build 前自动生成 wwwroot/filelist.txt,内容为所有 wwwroot 下的文件相对路径。无需手动维护清单,App 启动时可自动释放所有包内静态文件。
    /// </summary>
    /// <returns></returns>
    private async Task EnsureWwwrootExistsAsync()
    {
        string wwwrootPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");
        if (!Directory.Exists(wwwrootPath))
        {
            Directory.CreateDirectory(wwwrootPath);
            string[] files;
            try
            {
                using var listStream = await FileSystem.OpenAppPackageFileAsync("wwwroot/filelist.txt");
                using var reader = new StreamReader(listStream);
                var content = await reader.ReadToEndAsync();
                files = content.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
            }
            catch
            {
                files = new[] { "index.html" };
            }
            foreach (var file in files)
            {
                string destPath = Path.Combine(wwwrootPath, file);
                string packagePath = $"wwwroot/{file}";
                string destDir = Path.GetDirectoryName(destPath)!;
                if (!Directory.Exists(destDir)) Directory.CreateDirectory(destDir);
                using var stream = await FileSystem.OpenAppPackageFileAsync(packagePath);
                using var fileStream = File.Create(destPath);
                await stream.CopyToAsync(fileStream);
            }
        }
    }

六、注册静态路由

在服务器启动时注册路由:

api.AddStaticFiles("/", wwwrootPath);

现在访问:

http://127.0.0.1:8090

就会自动加载:

wwwroot/index.html

七、API 与前端协作

现在整个系统结构变成:

浏览器
   ↓
index.html
   ↓
fetch("/api/time")
   ↓
PicoServer API
   ↓
MAUI 本地逻辑

例如:

fetch("/api/product/list")

调用接口:

/api/product/list

这种方式有几个优势。


1 无跨域问题

因为:

页面
API

都在同一个服务器:

localhost:8090

2 本地离线运行

整个系统:

HTML
JS
API
数据库

都可以运行在本地。

不依赖外网。


3 Web 技术开发 UI

你可以直接使用:

Vue
React
Bootstrap
Admin Template

开发后台界面。


八、托管 Vue / React 前端

如果使用前端框架,例如 Vue。

构建后会生成:

dist
 ├─ index.html
 ├─ assets
 └─ js

只需要:

dist → wwwroot

即可。

访问:

http://localhost:8090

整个 Admin 系统即可运行。


九、静态资源缓存优化(推荐)

在 Web Server 中,静态资源通常可以缓存。

例如:

CSS
JS
图片

可以设置缓存 Header:

response.Headers.Add("Cache-Control", "public,max-age=3600");

表示:

浏览器缓存 1 小时

这样可以减少重复请求,提高页面加载速度。

推荐策略:

类型 缓存时间
HTML 不缓存
CSS / JS 1 小时
图片 1 天

十、WebView 壳模式

在 MAUI 中可以使用 WebView 打开本地页面:

<WebView Source="http://127.0.0.1:8090/index.html" />

此时应用结构变成:

MAUI App
   ↓
WebView
   ↓
localhost:8090
   ↓
PicoServer
   ↓
API

这种架构被称为:

Local Web Shell

优点:

UI 使用 Web 技术
逻辑使用 C#
跨平台

添加新的页面

Pages\WebViewPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiPicoAdmin.Pages.WebViewPage"
             Title="WebView">
    <WebView Source="http://127.0.0.1:8090/index.html" />
</ContentPage>

编辑 AppShell.xaml 添加路由

   <ShellContent
        Title="WebView"
        Icon="{StaticResource IconDashboard}"
        ContentTemplate="{DataTemplate pages:WebViewPage}"
        Route="webview" />

运行效果

image


十一、完整架构

现在整个系统已经变成一个完整 Web 架构:

            ┌───────────────────────┐
            │       Web UI          │
            │ Vue / React / Admin   │
            └──────────▲────────────┘
                       │ fetch
                       │
            ┌──────────┴────────────┐
            │      PicoServer       │
            │   Static + REST API   │
            └──────────▲────────────┘
                       │
                ┌──────┴──────┐
                │ Controller  │
                └──────▲──────┘
                       │
                ┌──────┴──────┐
                │  Service    │
                └──────▲──────┘
                       │
                ┌──────┴──────┐
                │  MAUI Logic │
                └─────────────┘

这种模式常用于:

工业设备管理系统
本地管理后台
嵌入式设备 Web UI
离线业务系统

十二、本篇总结

本篇我们为 PicoServer 增加了一个关键能力:

静态文件托管

新增能力包括:

静态文件服务器
HTML 页面托管
前端调用本地 API
Vue / React 前端整合
WebView 壳架构

此时我们的 MAUI 应用已经具备:

本地 Web Admin 系统

下一篇预告

下一篇我们将继续升级系统:

MAUI 嵌入式 Web 架构实战(五)

构建完整的 Web Admin 管理后台

内容包括:

Admin UI 框架选择
商品管理 CRUD 示例
API 与页面联动
表格、分页、表单设计

最终实现一个真正可用的 本地后台管理系统

posted @ 2026-03-06 17:26  AlexChow  阅读(19)  评论(0)    收藏  举报