From 1f87565d5aa8205ed8268a9ad83c6ab5bdfd3122 Mon Sep 17 00:00:00 2001
From: ShaoHua <345265198@qqcom>
Date: Mon, 13 Apr 2026 21:17:15 +0800
Subject: [PATCH] =?UTF-8?q?1.MAUI=20Android=E5=8F=AF=E4=BB=A5=E6=AD=A3?=
=?UTF-8?q?=E5=B8=B8=E6=98=BE=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Directory.Build.props | 2 +
Hua.Todo.slnx | 2 +-
README.md | 17 ++--
docs/manual/代码规范文档.md | 22 +++--
docs/manual/技术栈与模块.md | 22 +++--
docs/manual/版本记录.md | 14 ++-
.../Hua.Todo.Application.csproj | 2 +-
.../Hua.Todo.Avalonia.csproj | 27 +++++-
.../Platforms/Android/AndroidManifest.xml | 7 ++
.../Platforms/Android/MainActivity.cs | 24 +++++
src/Hua.Todo.Maui/Hua.Todo.Maui.csproj | 41 ++++----
src/Hua.Todo.Maui/MauiProgram.cs | 3 +-
.../Platforms/Android/AndroidManifest.xml | 10 +-
.../Android/MobileEmbeddedWebServerService.cs | 95 +++++++++++++++++--
14 files changed, 228 insertions(+), 60 deletions(-)
create mode 100644 src/Hua.Todo.Avalonia/Platforms/Android/AndroidManifest.xml
create mode 100644 src/Hua.Todo.Avalonia/Platforms/Android/MainActivity.cs
diff --git a/Directory.Build.props b/Directory.Build.props
index fd5e191..b62d5c2 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -14,6 +14,8 @@
true
+
+ true
diff --git a/Hua.Todo.slnx b/Hua.Todo.slnx
index b3eb81d..5a72ee9 100644
--- a/Hua.Todo.slnx
+++ b/Hua.Todo.slnx
@@ -11,7 +11,7 @@
-
+
diff --git a/README.md b/README.md
index 55e9c82..68dc3ba 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,16 @@
-# Hua.Todo 跨平台代办管理应用
+# Hua.Todo 跨平台代办管理应用 v1.2.8
-一个基于 WebView 容器(MAUI / Avalonia)+ 嵌入式 ASP.NET Core WebServer 架构开发的跨平台代办管理应用,支持 Windows、macOS、Android、iOS 和 Linux(预览)平台。通过 HTTP API 实现前后端通信,提供轻量、高效的任务管理体验。
+一个基于 WebView 容器(MAUI / Avalonia)+ 嵌入式 ASP.NET Core WebServer 架构开发的跨平台代办管理应用,支持 Windows、macOS、Android、iOS 和 Linux 平台。通过 HTTP API 实现前后端通信,提供轻量、高效的任务管理体验。
## 🚀 功能特点
### 核心功能
-- **跨平台支持**:基于 MAUI / Avalonia + WebView 架构,支持 Windows、macOS、Android、iOS 和 Linux(预览)
-- **任务管理**:支持创建、编辑、删除、完成状态切换
-- **优先级管理**:支持高、中、低三种优先级设置,通过颜色直观区分
-- **任务状态跟踪**:清晰标记任务完成状态,支持过滤查看(全部/进行中/已完成)
-- **本地数据持久化**:使用 SQLite 数据库保存数据,支持完全离线使用
-- **HTTP API 通信**:前后端通过 RESTful API 进行数据交互
-- **云同步(基础)**:支持手动配置服务端地址并登录后拉取云端任务(v1.2.0 为只读展示)
+- **跨平台支持**:基于 MAUI / Avalonia + WebView 架构,支持 Windows、macOS、Android、iOS 和 Linux。
+- **任务管理**:支持创建、编辑、删除、完成状态切换、子任务管理。
+- **云同步 (CloudSync)**:支持手动配置服务端地址并登录后拉取云端任务,支持安全策略(SecurityPolicy)配置。
+- **关键词检索**:支持按任务标题实时过滤,支持 Esc 清空,大小写不敏感。
+- **本地数据持久化**:使用 SQLite 数据库保存数据,支持 DateTime 兼容性解析。
+- **动态 API**:后端自动生成 RESTful API 并集成 Swagger UI,便于联调与调试。
## 📦 安装与使用
diff --git a/docs/manual/代码规范文档.md b/docs/manual/代码规范文档.md
index 6d31d48..ca68d24 100644
--- a/docs/manual/代码规范文档.md
+++ b/docs/manual/代码规范文档.md
@@ -1,4 +1,4 @@
-# Hua.Todo 代码规范文档 v1.1.0
+# Hua.Todo 代码规范文档 v1.2.8
## 1. 概述
本文档定义 Hua.Todo 项目的代码规范,包括 C#、JavaScript/TypeScript、Vue.js 和其他相关技术的编码标准。遵循这些规范有助于提高代码质量、可读性和可维护性。
@@ -10,21 +10,29 @@
- **避免缩写**: 除非是广泛认知的缩写(如 ID、URL、API)
- **一致性**: 在整个项目中保持命名风格一致
-### 2.2 注释规范
-- **公共 API 必须添加 XML 文档注释**
+### 2.2 注释规范 (强制)
+- **公共 API 必须添加 XML 文档注释** (包括 `public` / `protected` 的类、接口、方法、属性)
+- ** summary**:一句话说明用途
+- ** param / returns**:关键参数/返回值说明
+- ** 异常或副作用**:在 summary 中明确说明(例如会注册系统钩子/会启动后台服务)
- **复杂逻辑添加行内注释**
-- **避免注释显而易见的代码**
-- **保持注释与代码同步更新**
+- **禁止在日志或注释中输出密钥、Token、用户隐私信息**
### 2.3 代码格式化
-- **使用统一的代码格式化工具**
+- **使用统一的代码格式化工具** (VS / IDE 默认格式化)
- **保持一致的缩进和空格**
- **每行代码不超过 120 字符**
- **文件末尾保留一个空行**
## 3. C# 代码规范
-### 3.1 命名规范
+### 3.1 跨平台逻辑规范
+- **禁止混写 `#if`**:禁止在同一文件内混写多个平台的大段 `#if` 实现。
+- **优先使用 partial/接口**:应优先使用 `partial` 类、接口与平台目录分离。
+- **说明平台差异**:平台分离后的公共入口处必须说明“平台差异在哪里、默认实现是什么、为什么这么做”。
+
+### 3.2 异步/后台任务
+- **必须说明启动时机、错误处理策略、是否需要 UI 线程、以及是否可并发/可重入**。
#### 类和接口
```csharp
diff --git a/docs/manual/技术栈与模块.md b/docs/manual/技术栈与模块.md
index 3e6ee57..24f2bb0 100644
--- a/docs/manual/技术栈与模块.md
+++ b/docs/manual/技术栈与模块.md
@@ -3,19 +3,19 @@
## 🛠️ 技术栈
### 后端技术栈
-- **开发语言**:C# 10
-- **框架**:.NET 10
+- **开发语言**:C# 13 (基于 .NET 10)
+- **框架**:.NET 10 (Preview/Early Adopter)
- **UI 框架**:MAUI(移动端/部分桌面) + Avalonia(桌面端)
- **Web 服务器**:Kestrel (ASP.NET Core 内置)
-- **API 框架**:ASP.NET Core Web API
-- **数据访问**:Entity Framework Core
+- **API 框架**:ASP.NET Core Web API (支持动态 API 生成)
+- **数据访问**:Entity Framework Core 10.0
- **数据库**:SQLite (本地存储)
- **依赖注入**:Microsoft.Extensions.DependencyInjection
### 前端技术栈
-- **开发语言**:TypeScript
+- **开发语言**:TypeScript 5+
- **框架**:Vue.js 3
-- **构建工具**:Vite
+- **构建工具**:Vite 5+
- **HTTP 客户端**:Axios
- **状态管理**:Pinia
- **UI 组件库**:Element Plus / Vant (移动端)
@@ -24,13 +24,17 @@
## 🎯 核心模块说明
### Hua.Todo.Core
-领域实体层,定义核心实体(TaskEntity)、枚举(TaskPriority)以及仓储接口(ITaskRepository)。
+领域实体层,定义核心实体(TaskEntity)、安全策略实体(SecurityPolicyEntity)、枚举(TaskPriority)以及仓储接口(ITaskRepository)。
### Hua.Todo.Application
-应用层实现,包含业务逻辑、动态 API 生成逻辑、EF Core 数据库上下文以及具体的服务实现(TaskService)。
+应用层实现,包含:
+- **业务逻辑**:TaskService 实现。
+- **动态 API**:基于 Middleware 的动态 API 生成逻辑。
+- **云同步 (CloudSync)**:包含 Auth 验证、同步服务、安全策略管理等。
+- **数据访问**:EF Core 数据库上下文(TodoDbContext)与迁移。
### Hua.Todo.Host
-后端 API 宿主,提供运行环境和配置,是后端服务的启动入口。
+后端 API 宿主,作为独立 Server 运行时提供运行环境和配置。
### Hua.Todo.Web
前端 Web 项目,基于 Vue.js 3 + TypeScript + Vite,提供用户界面,通过 HTTP API 与后端通信。
diff --git a/docs/manual/版本记录.md b/docs/manual/版本记录.md
index 89ecab3..123f212 100644
--- a/docs/manual/版本记录.md
+++ b/docs/manual/版本记录.md
@@ -9,13 +9,23 @@
- v1.1.0:MAUI + WebView 跨平台版本
- v1.2.0 (规划中):Linux 支持与增强功能
-### v1.2.0(开发中,2026-04-07)
+### v1.2.8 (2026-04-13)
+
+- **云同步增强**:在 `Hua.Todo.Application` 中深度集成 `CloudSync` 模块,支持权限验证、安全策略(SecurityPolicy)与任务同步 DTO。
+- **动态 API 增强**:完善 `DynamicApi` 逻辑,支持 Swagger 自动过滤与中间件拦截。
+- **代码规范同步**:强制执行 XML 文档注释与跨平台逻辑分离规范,更新 `.trae/rules` 规则库。
+- **多平台构建优化**:优化 `Directory.Build.props` 与 `Directory.Build.targets`,精细化控制各平台(Windows/Android/iOS/Linux)的构建开关与依赖。
+- **版本号统一**:全项目版本号提升至 `v1.2.8`,同步更新各平台安装包与发布脚本。
+
+### v1.2.0(2026-04-07)
- **Linux 官方支持**:新增 `Hua.Todo.Avalonia` 项目,正式适配 Linux 平台,同时支持 Windows 和 macOS。
- **Avalonia 桌面交互**:增加托盘菜单(显示/退出)、关闭隐藏到托盘、Windows 全局热键唤起主窗口、热键配置本地持久化;并对齐 Avalonia 的 appsettings 默认值。
- **关键词检索**:主界面增加搜索框,按任务标题实时过滤;采用“命中即显示(含上下文)”策略;支持 Esc 清空;英文大小写不敏感。
- **云同步(基础可用)**:新增“云同步设置”弹窗,支持手动配置服务端地址(格式校验 + 保存时可达性/风险提示);登录成功后拉取云端任务并刷新主界面(v1.2.0 为只读展示);401/403 时会自动清会话并弹出登录入口。
- **MAUI(Windows)内嵌 API 文档**:Debug 模式下,内嵌 WebServer 默认提供 Swagger UI(`{HostUrl}/swagger`)与 OpenAPI JSON(`{HostUrl}/swagger/v1/swagger.json`),便于本地接口调试。
+- **Android 启动稳定性修复**:在 AndroidManifest 中移除 `androidx.startup.InitializationProvider` 自动初始化入口,规避 `androidx.lifecycle.ProcessLifecycleInitializer` 缺失导致的启动崩溃(`NoClassDefFoundError`)。
+- **MAUI Android 调试配置修复**:在 `Hua.Todo.Maui.csproj` 中显式启用 `AndroidApplication`,并将调试架构配置从 `AndroidSupportedAbis` 切换为 `RuntimeIdentifiers=android-x64`,减少 Visual Studio 启动 Android 调试时的项目识别与模拟器架构问题。
- **Swagger 输出补齐 Dynamic API**:任务管理等 Dynamic API 端点会出现在 `swagger.json` 中,避免“接口缺失”导致联调困难。
- **SQLite DateTime 兼容修复**:新增 `LenientUtcDateTimeStringConverter`,本地数据库中若存在历史遗留的 DateTime “ticks/时间戳字符串”脏数据,读取时将被兼容解析,避免 `/api/task` 等查询因单条坏数据整体失败。
- **SPA 路由回落行为修复**:当 Release/非 Debug 未启用 Swagger 时,`/swagger` 不再被当作“后端专用路径”排除,访问会按 SPA 路由规则回落到 `/index.html`,避免直接 404。
@@ -26,7 +36,7 @@
- **Windows WebView2 Runtime 误判修复**:当系统已安装 WebView2 Runtime 但发布产物缺少/裁剪 WebView2 托管程序集时,旧检测逻辑会误判为“未安装”;现改为优先从常见安装目录探测 Evergreen 版本,避免阻断主界面加载。
- **Windows 三件套开发体验**:新增 `start-host.ps1` / `start-dev.ps1`,并在 MAUI 中约定 `IsUsingStatic=false` 时不启动内置 WebServer,避免注入覆盖 Vite 的 `/api -> 5173` 代理配置。
- **文档与部署指南**:新增 `docs/manual/部署文档.md`,详细说明开发环境搭建、多平台发布流程(Windows/Linux/Docker)以及关键配置项;并在技术设计文档中建立链接。
-- **用户文档完善**:新增 `docs/manual/新手指南.md` 和 `docs/manual/用户指南.md`,提供详细的使用说明和操作指南。
+- **用户文档完善**:在规划中新增了 `docs/manual/新手指南.md` 和 `docs/manual/用户指南.md`。
27→
28→### v1.1.1 (2026-04-06)
diff --git a/src/Hua.Todo.Application/Hua.Todo.Application.csproj b/src/Hua.Todo.Application/Hua.Todo.Application.csproj
index c696ada..7d78d5a 100644
--- a/src/Hua.Todo.Application/Hua.Todo.Application.csproj
+++ b/src/Hua.Todo.Application/Hua.Todo.Application.csproj
@@ -4,7 +4,7 @@
true
net10.0
- net10.0;net10.0-android;net10.0-ios;net10.0-maccatalyst
+ net10.0;net10.0-android36.0;net10.0-ios;net10.0-maccatalyst
enable
enable
Library
diff --git a/src/Hua.Todo.Avalonia/Hua.Todo.Avalonia.csproj b/src/Hua.Todo.Avalonia/Hua.Todo.Avalonia.csproj
index d55a0be..2c448aa 100644
--- a/src/Hua.Todo.Avalonia/Hua.Todo.Avalonia.csproj
+++ b/src/Hua.Todo.Avalonia/Hua.Todo.Avalonia.csproj
@@ -1,10 +1,13 @@
-
+
WinExe
net10.0
net10.0-windows10.0.19041.0
net10.0
net10.0
+
+ $(TargetFrameworks);net10.0-android
+
enable
app.manifest
true
@@ -14,6 +17,21 @@
false
+
+ 21.0
+ com.hua.todo
+ 代办
+ true
+ Exe
+
+
+ False
+ False
+ True
+
+
@@ -21,17 +39,24 @@
+
+
+
+
None
All
+
+
+
diff --git a/src/Hua.Todo.Avalonia/Platforms/Android/AndroidManifest.xml b/src/Hua.Todo.Avalonia/Platforms/Android/AndroidManifest.xml
new file mode 100644
index 0000000..1ca80b4
--- /dev/null
+++ b/src/Hua.Todo.Avalonia/Platforms/Android/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/Hua.Todo.Avalonia/Platforms/Android/MainActivity.cs b/src/Hua.Todo.Avalonia/Platforms/Android/MainActivity.cs
new file mode 100644
index 0000000..5a7aab5
--- /dev/null
+++ b/src/Hua.Todo.Avalonia/Platforms/Android/MainActivity.cs
@@ -0,0 +1,24 @@
+using Android.App;
+using Android.Content.PM;
+using Avalonia;
+using Avalonia.Android;
+
+namespace Hua.Todo.Avalonia.Platforms.Android;
+
+///
+/// Avalonia Android 平台入口 Activity。
+///
+[Activity(
+ Label = "代办",
+ Theme = "@style/AvaloniaMainActivityTheme",
+ Icon = "@drawable/icon",
+ MainLauncher = true,
+ ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)]
+public class MainActivity : AvaloniaMainActivity
+{
+ protected override AppBuilder CustomizeAppBuilder(AppBuilder builder)
+ {
+ return base.CustomizeAppBuilder(builder)
+ .WithInterFont();
+ }
+}
diff --git a/src/Hua.Todo.Maui/Hua.Todo.Maui.csproj b/src/Hua.Todo.Maui/Hua.Todo.Maui.csproj
index dfc3f72..72ff2a5 100644
--- a/src/Hua.Todo.Maui/Hua.Todo.Maui.csproj
+++ b/src/Hua.Todo.Maui/Hua.Todo.Maui.csproj
@@ -14,6 +14,7 @@
Exe
+ true
Hua.Todo.Maui
true
true
@@ -28,7 +29,7 @@
代办
- com.companyname.Hua.Todo.maui
+ com.hua.todo
@@ -70,16 +71,12 @@
true
apk
False
-
- True
- True
- Assemblies
- False
-
- False
- False
- True
- x86_64
+
+ False
+ False
+ True
+ android-x64
false
@@ -166,7 +163,7 @@
-
+
@@ -184,21 +181,33 @@
- wwwroot/%(RecursiveDir)%(Filename)%(Extension)
+ $([System.String]::Copy('wwwroot/%(RecursiveDir)%(Filename)%(Extension)').Replace('\', '/'))
-
+
- <_MauiWwwrootFiles Include="$(MSBuildProjectDirectory)\wwwroot\**\*" />
+
+ <_MauiWwwrootFiles Include="wwwroot\**\*" />
+
- wwwroot/%(RecursiveDir)%(Filename)%(Extension)
+ wwwroot\%(RecursiveDir)%(Filename)%(Extension)
+ $([System.String]::Copy('wwwroot/%(RecursiveDir)%(Filename)%(Extension)').Replace('\', '/'))
+
+
+
+
+ index.html
+ index.html
+
+
diff --git a/src/Hua.Todo.Maui/MauiProgram.cs b/src/Hua.Todo.Maui/MauiProgram.cs
index 4238163..a22d77b 100644
--- a/src/Hua.Todo.Maui/MauiProgram.cs
+++ b/src/Hua.Todo.Maui/MauiProgram.cs
@@ -100,8 +100,9 @@ public static partial class MauiProgram
{
try
{
- InitializeDatabase(app.Services, connectionString);
+ // 优先启动内嵌 WebServer,确保 WebView 加载时不再 ERR_CONNECTION_REFUSED。
await StartWebServer(app.Services);
+ InitializeDatabase(app.Services, connectionString);
}
catch (Exception ex)
{
diff --git a/src/Hua.Todo.Maui/Platforms/Android/AndroidManifest.xml b/src/Hua.Todo.Maui/Platforms/Android/AndroidManifest.xml
index c0ed746..41a64d9 100644
--- a/src/Hua.Todo.Maui/Platforms/Android/AndroidManifest.xml
+++ b/src/Hua.Todo.Maui/Platforms/Android/AndroidManifest.xml
@@ -1,6 +1,8 @@
-
-
-
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/src/Hua.Todo.Maui/Platforms/Android/MobileEmbeddedWebServerService.cs b/src/Hua.Todo.Maui/Platforms/Android/MobileEmbeddedWebServerService.cs
index 5d08f2d..8deb806 100644
--- a/src/Hua.Todo.Maui/Platforms/Android/MobileEmbeddedWebServerService.cs
+++ b/src/Hua.Todo.Maui/Platforms/Android/MobileEmbeddedWebServerService.cs
@@ -55,7 +55,9 @@ public sealed class MobileEmbeddedWebServerService : IEmbeddedWebServerService
if (_listener != null) return Task.CompletedTask;
_cts = new CancellationTokenSource();
- _listener = new TcpListener(IPAddress.Loopback, _appSettings.WebServer.Port);
+ // 使用 IPAddress.Any 而非 Loopback,避免 localhost 解析到 IPv6 [::1] 时连接被拒绝。
+ // 同时确保在 Android 模拟器/设备上 localhost 能正确连通。
+ _listener = new TcpListener(IPAddress.Any, _appSettings.WebServer.Port);
_listener.Start();
_acceptLoop = Task.Run(() => AcceptLoopAsync(_cts.Token));
@@ -257,12 +259,42 @@ public sealed class MobileEmbeddedWebServerService : IEmbeddedWebServerService
return;
}
+ Console.WriteLine($"[WebServer] Serving static: {path} -> {assetPath}");
+
if (!TryOpenAsset(assetPath, out var assetStream))
{
+ // 如果 wwwroot/path 找不到,尝试直接加载 path(应对路径扁平化)
+ var fallbackAssetPath = path.TrimStart('/');
+ if (!string.IsNullOrEmpty(fallbackAssetPath) && !fallbackAssetPath.StartsWith("wwwroot", StringComparison.OrdinalIgnoreCase))
+ {
+ Console.WriteLine($"[WebServer] {assetPath} not found, trying fallback path: {fallbackAssetPath}");
+ if (TryOpenAsset(fallbackAssetPath, out assetStream))
+ {
+ await using (assetStream)
+ {
+ await WriteStreamAsync(stream, 200, assetStream, GetContentType(path), token);
+ }
+ return;
+ }
+ }
+
if (!Path.HasExtension(path))
{
- assetPath = "wwwroot/index.html";
- if (TryOpenAsset(assetPath, out assetStream))
+ var fallbackHtmlPath = "wwwroot/index.html";
+ Console.WriteLine($"[WebServer] {assetPath} not found, trying SPA fallback: {fallbackHtmlPath}");
+ if (TryOpenAsset(fallbackHtmlPath, out assetStream))
+ {
+ await using (assetStream)
+ {
+ await WriteStreamAsync(stream, 200, assetStream, "text/html; charset=utf-8", token);
+ }
+ return;
+ }
+
+ // 再次尝试不带 wwwroot 的 index.html
+ fallbackHtmlPath = "index.html";
+ Console.WriteLine($"[WebServer] {assetPath} not found, trying SPA fallback: {fallbackHtmlPath}");
+ if (TryOpenAsset(fallbackHtmlPath, out assetStream))
{
await using (assetStream)
{
@@ -272,6 +304,7 @@ public sealed class MobileEmbeddedWebServerService : IEmbeddedWebServerService
}
}
+ Console.WriteLine($"[WebServer] 404 Not Found: {assetPath}");
await WriteTextAsync(stream, 404, "Not Found", "text/plain; charset=utf-8", token);
return;
}
@@ -286,11 +319,43 @@ public sealed class MobileEmbeddedWebServerService : IEmbeddedWebServerService
{
try
{
- stream = global::Android.App.Application.Context.Assets.Open(assetPath);
+ // Android AssetManager.Open() 路径不能以 / 开头,且必须使用正斜杠
+ var normalizedPath = assetPath.TrimStart('/').Replace('\\', '/');
+ stream = global::Android.App.Application.Context.Assets.Open(normalizedPath);
return true;
}
- catch
+ catch (Exception ex)
{
+ Console.WriteLine($"[WebServer] Failed to open asset '{assetPath}': {ex.Message}");
+
+ // 只在 wwwroot/index.html 失败时尝试列出文件,帮助排查打包结构
+ if (assetPath.EndsWith("index.html", StringComparison.OrdinalIgnoreCase))
+ {
+ try
+ {
+ // 列出 root assets 和 wwwroot 目录,帮助排查打包结构
+ var rootAssets = global::Android.App.Application.Context.Assets.List("");
+ if (rootAssets != null)
+ {
+ Console.WriteLine($"[WebServer] Root Assets: {string.Join(", ", rootAssets)}");
+ }
+
+ var assets = global::Android.App.Application.Context.Assets.List("wwwroot");
+ if (assets != null && assets.Length > 0)
+ {
+ Console.WriteLine($"[WebServer] Assets in 'wwwroot/': {string.Join(", ", assets)}");
+ }
+ else
+ {
+ Console.WriteLine("[WebServer] 'wwwroot/' directory is empty or not found in assets.");
+ }
+ }
+ catch (Exception listEx)
+ {
+ Console.WriteLine($"[WebServer] Failed to list assets: {listEx.Message}");
+ }
+ }
+
stream = Stream.Null;
return false;
}
@@ -423,6 +488,8 @@ public sealed class MobileEmbeddedWebServerService : IEmbeddedWebServerService
var requestLine = await reader.ReadLineAsync(token);
if (string.IsNullOrWhiteSpace(requestLine)) throw new InvalidOperationException("empty request line");
+ Console.WriteLine($"[WebServer] Raw Request Line: {requestLine}");
+
var parts = requestLine.Split(' ');
if (parts.Length < 2) throw new InvalidOperationException("invalid request line");
@@ -430,11 +497,19 @@ public sealed class MobileEmbeddedWebServerService : IEmbeddedWebServerService
var target = parts[1].Trim();
string path;
- if (Uri.TryCreate(target, UriKind.Absolute, out var absoluteUri) &&
- (absoluteUri.Scheme.Equals("http", StringComparison.OrdinalIgnoreCase) ||
- absoluteUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)))
+ if (target.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
+ target.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
{
- path = absoluteUri.AbsolutePath;
+ // WebView 有时会发送绝对 URL(例如 GET http://localhost:5057/ HTTP/1.1)
+ if (Uri.TryCreate(target, UriKind.Absolute, out var absoluteUri))
+ {
+ path = absoluteUri.AbsolutePath;
+ Console.WriteLine($"[WebServer] Parsed path from absolute URI: {path}");
+ }
+ else
+ {
+ path = target;
+ }
}
else if (target.StartsWith("//", StringComparison.Ordinal) &&
Uri.TryCreate("http:" + target, UriKind.Absolute, out var authorityUri))
@@ -451,6 +526,8 @@ public sealed class MobileEmbeddedWebServerService : IEmbeddedWebServerService
if (string.IsNullOrEmpty(path)) path = "/";
if (!path.StartsWith("/", StringComparison.Ordinal)) path = "/" + path;
+ Console.WriteLine($"[WebServer] Final Resolved Path: {path}");
+
var headers = new Dictionary(StringComparer.OrdinalIgnoreCase);
string? line;
while (!string.IsNullOrEmpty(line = await reader.ReadLineAsync(token)))