PicoServer
Source Code URL:
https://github.com/densen2014/MauiPicoAdmin
In the previous articles of this series, we have gradually built a complete embedded web service architecture based on MAUI + PicoServer, which includes the following components:
- PicoServer local web server
- REST API interface system
- Web Admin management backend
- Permission authentication system
- WebSocket real-time communication
As the system continues to expand, a new architectural challenge emerges:
When the number of APIs keeps growing, how can we make the server automatically discover and register APIs instead of manually registering each interface?
For example, the following API modules may emerge in the system one after another:
/api/system/*/api/device/*/api/order/*/api/storage/*/api/user/*
If every API needs to be registered manually like this:
api.AddRoute("/api/system/info", SystemInfo, "GET");
api.AddRoute("/api/device/start", DeviceStart, "POST");
api.AddRoute("/api/order/list", OrderList, "GET");
api.AddRoute("/api/storage/add", StorageAdd, "POST");
When the number of interfaces reaches dozens or even hundreds:
The server startup code will become extremely cluttered.
Therefore, in this article, we will implement a more elegant architecture:
API Architecture with Automatic Controller Discovery
Core capabilities include:
- Marking Controllers with
[ApiController] - Marking APIs with
[ApiRoute] - Scanning Controllers via reflection
- Automatic API registration
- Modular interface system
The final development experience will be as follows:
[ApiController]
public class DeviceController
{
[ApiRoute("/api/device/list","GET")]
public object List()
{
return DeviceManager.GetDevices();
}
}
There is no longer a need to manually register APIs.
When the server starts, it will automatically complete the following steps:
Scan Controllers
↓
Scan API methods
↓
Automatically register Routes
I. Target Architecture
The final server structure is as follows:
MAUI App
│
└── PicoServer
│
├── API Core
│
├── Controller Scanner
│
└── Controllers
├── SystemController
├── DeviceController
├── OrderController
└── StorageController
Server startup process:
Scan Controllers
↓
Scan APIs
↓
Automatically register Routes
II. Define the ApiController Attribute
First, define the Attribute for marking Controllers:
[AttributeUsage(AttributeTargets.Class)]
public class ApiControllerAttribute : Attribute
{
}
Usage:
[ApiController]
public class DeviceController
{
}
During scanning, the server will only load classes marked with [ApiController].
III. Define the API Route Attribute
Describe each API using an Attribute:
namespace MauiPicoAdmin;
[AttributeUsage(AttributeTargets.Method)]
public class ApiRouteAttribute : Attribute
{
public string Path { get; }
public string? Method { get; }
public ApiRouteAttribute(string path)
{
Path = path;
}
public ApiRouteAttribute(string path, string? method)
{
Path = path;
Method = method;
}
}
Usage:
[ApiRoute("/api/system/info","GET")]
public object Info()
{
return new
{
version = "1.0",
time = DateTime.Now
};
}
IV. Controller Example
A complete Controller implementation:
[ApiController]
public class DeviceController
{
[ApiRoute("/api/device/list","GET")]
public object List()
{
return new
{
count = 5
};
}
[ApiRoute("/api/device/start","POST")]
public object Start()
{
return new
{
ok = true
};
}
}
Once the Controller is written, the API definition is complete.
V. Automatically Scan Controllers
Scan the assembly when the server starts:
var asm = Assembly.GetExecutingAssembly();
var controllers = asm.GetTypes()
.Where(t => t.GetCustomAttribute<ApiControllerAttribute>() != null);
This way, we can find all classes marked with [ApiController].
VI. Instantiate Controllers
Iterate through the discovered Controllers:
foreach (var type in controllers)
{
var controller = Activator.CreateInstance(type);
RegisterController(controller);
}
VII. Scan API Methods
Scan the methods within each Controller:
void RegisterController(object controller)
{
var methods = controller.GetType().GetMethods();
foreach (var method in methods)
{
var route = method.GetCustomAttribute<ApiRouteAttribute>();
if (route == null)
continue;
RegisterApi(route, controller, method);
}
}
This step finds all methods marked with [ApiRoute].
VIII. Automatically Register APIs
Unifiedly register interfaces in PicoServer using api.AddRoute():
The implementation code is as follows:
void RegisterApi(ApiRouteAttribute route, object controller, MethodInfo method)
{
Func<HttpListenerRequest, HttpListenerResponse, Task> handler = (request, response) =>
{
var result = method.Invoke(controller, new object[] { request, response });
return result as Task ?? Task.CompletedTask;
};
api.AddRoute(route.Path, handler, route.Method);
}
This automatically converts Controller methods into API Routes.
IX. Complete Runtime Process
Execute the following method when the server starts:
ScanControllers();
The complete process is as follows:
Scan the assembly
↓
Find [ApiController] classes
↓
Scan methods
↓
Find [ApiRoute] methods
↓
Call api.AddRoute()
↓
Complete API registration
Eventually, all APIs are loaded automatically.
X. Final Development Experience
When developing APIs, you only need to write Controllers:
[ApiController]
public class OrderController
{
[ApiRoute("/api/order/list","GET")]
public object List()
{
return OrderService.GetOrders();
}
}
After the server starts, the API GET /api/order/list is registered automatically.
Developers no longer need to manually write any api.AddRoute(...) code, making the code extremely concise.
XI. Architectural Advantages
This API architecture brings the following benefits:
1. Automatic API Registration
Eliminates the need for a large number of api.AddRoute(...) code lines.
2. Modular Development
Each module has its own independent Controller:
SystemControllerDeviceControllerOrderControllerStorageControllerUserController
Resulting in a very clear structure.
3. Simple Expansion
Adding a new API only requires adding a method marked with [ApiRoute], with no need to modify server code.
4. Foundation for Plug-in Architecture
Although this article only scans the current assembly, it can be extended in the future to:
- Scan external DLLs
- Load Controllers from external assemblies
Thus implementing a true plug-in API system.
XII. Next Article Preview
In the next article, we will continue to upgrade the architecture with:
PicoServer + PWA Offline System
We will implement:
- Browser offline operation
- Service Worker
- Local data caching
- Offline Web Admin
- MAUI Web Shell
Ultimately building a complete solution that integrates:
- Offline Web App
- Local API
- MAUI Web Shell
A true local web application platform.
Top comments (0)