DEV Community

ben
ben

Posted on

Practical Combat of MAUI Embedded Web Architecture (4) PicoServer Static File Hosting and Integration with Vue/React Frontends

PicoServer
Source Code URL:
https://github.com/densen2014/MauiPicoAdmin

In the previous articles, we have implemented the core capabilities of PicoServer in MAUI:

  1. Embedding a local HTTP service in a .NET MAUI application
  2. Building local REST APIs with PicoServer
  3. 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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:

  1. Define the path
public readonly string wwwrootPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wwwroot");
Enter fullscreen mode Exit fullscreen mode
  1. Edit the project file to automatically generate wwwroot/filelist.txt before 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>
Enter fullscreen mode Exit fullscreen mode

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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

VI. Register Static Routes

Register the route when starting the server:

api.AddStaticFiles("/", wwwrootPath);
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

For example:

fetch("/api/product/list")
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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");
Enter fullscreen mode Exit fullscreen mode

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" />
Enter fullscreen mode Exit fullscreen mode

At this point, the application structure becomes:

MAUI App
   ↓
WebView
   ↓
localhost:8090
   ↓
PicoServer
   ↓
API
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Edit AppShell.xaml to add a route

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

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 │
                └─────────────┘
Enter fullscreen mode Exit fullscreen mode

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:

  1. Static file server
  2. HTML page hosting
  3. Frontend calling local APIs
  4. Vue/React frontend integration
  5. 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:

  1. Admin UI framework selection
  2. Product management CRUD example
  3. API and page interaction
  4. Table, pagination, and form design

Ultimately, we will implement a truly usable local backend management system.

Top comments (0)