PicoServer
Source Code URL:
https://github.com/densen2014/MauiPicoAdmin
In the previous articles, we have implemented the core capabilities of PicoServer in MAUI:
- Embedding a local HTTP service in a .NET MAUI application
- Building local REST APIs with PicoServer
- Implementing a layered Controller/Service architecture
At this point, our application already has the capability of a local API Server.
However, a complete application system usually consists of two parts:
- Backend APIs
- Frontend interfaces
Having only APIs is still not enough to build a complete application.
Therefore, in this article, we will further upgrade PicoServer to equip it with static file hosting capabilities.
In other words, we will enable the MAUI application to directly run web frontends such as:
- HTML
- Vue
- React
- Admin Dashboard
Ultimately, we will implement a complete local Web Admin system.
The overall architecture is as follows:
Browser / WebView
│
▼
http://localhost:8090
│
▼
Web Admin
│
fetch API
│
▼
PicoServer
│
▼
MAUI Business Logic
I. What is Static File Hosting?
Static file hosting means that the server directly returns files. For example:
| URL | File Returned |
|---|---|
| / | index.html |
| /css/app.css | CSS file |
| /js/app.js | JavaScript file |
For example, when accessing:
http://127.0.0.1:8090/index.html
The server will read the file:
wwwroot/index.html
Then return it to the browser.
This mode is exactly the same as traditional web servers (Nginx / Apache / ASP.NET).
II. Create a Static Resource Directory
First, create a directory in the MAUI project:
Resources\Raw\wwwroot
Recommended directory structure:
wwwroot
├─ index.html
├─ css
│ └─ app.css
├─ js
│ └─ app.js
└─ images
This directory name is very common in web frameworks, such as:
- ASP.NET Core
- Spring Boot
- Node.js
All use a similar directory as the static file directory.
III. Create a Test Page
Create a simple page:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>PicoServer Admin</title>
</head>
<body>
<h1>PicoServer Web Admin</h1>
<button onclick="loadTime()">Get Server Time</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>
This page will call the API we implemented earlier:
/api/time
IV. Implement Static File Reading
Place the static file (index.html) in the project directory Resources\Raw\wwwroot.
The following steps are required to implement the file system:
- Define the path
public readonly string wwwrootPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");
-
Edit the project file to automatically generate
wwwroot/filelist.txtbefore each Debug/Build. The file contains the relative paths of all files under wwwroot. There is no need to manually maintain the list—all packaged static files can be automatically released when the app starts.
<!-- Automatically generate wwwroot/filelist.txt before each Debug. The file contains relative paths of all files in the wwwroot directory. -->
<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" />
</Target>
V. Release Resources to Writable Folder
Since we are using MAUI, except for the Windows platform which allows direct access, other platforms require manual release of resources to a writable folder.
Add the following method: All packaged static files can be automatically released when the app starts.
public PicoServerHost()
{
EnsureWwwrootExistsAsync().Wait(); // Add this line
RegisterRoutes();
api.StartServer();
}
/// <summary>
/// Automatically generate wwwroot/filelist.txt before each Debug/Build. The file contains relative paths of all files under wwwroot. No need to manually maintain the list—all packaged static files can be automatically released when the app starts.
/// </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);
}
}
}
VI. Register Static Routes
Register the route when starting the server:
api.AddStaticFiles("/", wwwrootPath);
Now, accessing the address:
http://127.0.0.1:8090
will automatically load the file:
wwwroot/index.html
VII. Collaboration Between APIs and Frontend
Now the entire system structure becomes:
Browser
↓
index.html
↓
fetch("/api/time")
↓
PicoServer API
↓
MAUI Local Logic
For example:
fetch("/api/product/list")
calls the interface:
/api/product/list
This approach has several advantages:
1. No Cross-Origin Issues
Because both the pages and APIs are hosted on the same server:
localhost:8090
2. Local Offline Operation
The entire system—including HTML, JS, APIs, and database—can run locally without relying on an external network.
3. UI Development with Web Technologies
You can directly use web technologies such as:
- Vue
- React
- Bootstrap
- Admin Template
to develop backend interfaces.
VIII. Hosting Vue/React Frontends
If you use a frontend framework such as Vue, the build process will generate a dist directory with the following structure:
dist
├─ index.html
├─ assets
└─ js
You only need to copy all contents from the dist directory to the wwwroot directory.
Then accessing the address:
http://localhost:8090
will launch the entire Admin system.
IX. Static Resource Caching Optimization (Recommended)
In web servers, static resources can usually be cached. For example, resources like CSS, JS, and images can have a cache header set:
response.Headers.Add("Cache-Control", "public,max-age=3600");
This means the browser will cache the resources for 1 hour, which reduces repeated requests and improves page loading speed.
Recommended caching strategy:
| Resource Type | Cache Duration |
|---|---|
| HTML | No caching |
| CSS / JS | 1 hour |
| Images | 1 day |
X. WebView Shell Mode
In MAUI, you can use WebView to open local pages:
<WebView Source="http://127.0.0.1:8090/index.html" />
At this point, the application structure becomes:
MAUI App
↓
WebView
↓
localhost:8090
↓
PicoServer
↓
API
This architecture is called Local Web Shell, and it has the following advantages:
- UI developed with web technologies
- Logic implemented with C#
- Cross-platform compatibility
Add a new page: 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>
Edit AppShell.xaml to add a route
<ShellContent
Title="WebView"
Icon="{StaticResource IconDashboard}"
ContentTemplate="{DataTemplate pages:WebViewPage}"
Route="webview" />
XI. Complete Architecture
Now the entire system has evolved into a complete web architecture:
┌───────────────────────┐
│ Web UI │
│ Vue / React / Admin │
└──────────▲────────────┘
│ fetch
│
┌──────────┴────────────┐
│ PicoServer │
│ Static + REST API │
└──────────▲────────────┘
│
┌──────┴──────┐
│ Controller │
└──────▲──────┘
│
┌──────┴──────┐
│ Service │
└──────▲──────┘
│
┌──────┴──────┐
│ MAUI Logic │
└─────────────┘
This pattern is commonly used in scenarios such as:
- Industrial device management systems
- Local management backends
- Embedded device web UIs
- Offline business systems
XII. Summary of This Article
In this article, we added a key capability to PicoServer: static file hosting.
The newly added capabilities include:
- Static file server
- HTML page hosting
- Frontend calling local APIs
- Vue/React frontend integration
- WebView shell architecture
At this point, our MAUI application has evolved into a local Web Admin system.
Next Article Preview
In the next article, we will continue to upgrade the system with:
Practical Combat of MAUI Embedded Web Architecture (5)
Building a Complete Web Admin Management Backend
Content will include:
- Admin UI framework selection
- Product management CRUD example
- API and page interaction
- Table, pagination, and form design
Ultimately, we will implement a truly usable local backend management system.

Top comments (0)