PicoServer
Source Code URL:
https://github.com/densen2014/MauiPicoAdmin
I. From Simple APIs to a Scalable Architecture
In the previous article Practical Combat of MAUI Embedded Web Architecture (II), we have successfully built multiple PicoServer APIs, such as:
/api/time/api/product/list/api/product/detail
These interfaces are already capable of:
- Handling HTTP requests
- Returning JSON data
- Supporting simple parameter reading
For example:
Access address: http://127.0.0.1:8090/api/product/list
Response:
{
"code":0,
"message":"ok",
"data":[]
}
This means our MAUI application has been equipped with local REST API service capabilities.
However, as the number of interfaces grows, new problems will gradually emerge:
- All APIs are written in a single class
- HTTP logic is mixed with business logic
- Inconsistent response formats
- Error handling becomes difficult to maintain
For example:
class PicoAdmin
{
ProductList()
ProductDetail()
ProductAdd()
ProductDelete()
}
When the number of interfaces reaches:
- 50+
- 100+
- 200+
The code will become difficult to maintain.
Therefore, this article will perform an architecture upgrade on PicoServer.
The goal is to build a:
scalable local REST API framework structure
Core objectives include:
- API modularization
- Controller/Service layered architecture
- Unified response structure
- Unified exception handling
- Clear project structure
II. Core Architecture of the REST API Framework
The upgraded API architecture is as follows:
HTTP Request
│
▼
PicoServer
│
Router
│
Controller
│
Service
│
Data / Device / Logic
Responsibilities of each layer:
| Layer | Function |
|---|---|
| Router | Route distribution |
| Controller | HTTP request handling |
| Service | Business logic processing |
| Data / Device | Data or device operations |
This structure is very similar to common web frameworks (e.g., ASP.NET Core).
III. Recommended Project Directory Structure
To make the API more maintainable, the following directory structure can be adopted:
Server
├─ Controllers
│ ├─ ProductController.cs
│ └─ SystemController.cs
│
├─ Services
│ └─ ProductService.cs
│
├─ Models
│ └─ ApiResult.cs
│
└─ PicoServerHost.cs
Directory responsibility description:
| Directory | Function |
|---|---|
| Controllers | API route handling |
| Services | Business logic implementation |
| Models | Data models definition |
| PicoServerHost | Server startup and route registration |
This structure ensures clear, modularized code.
IV. Unified API Response Structure
In an API system, a unified response format is extremely important.
First, we define a response model:
ApiResult.cs
public class ApiResult
{
public int Code { get; set; }
public string Message { get; set; }
public object Data { get; set; }
public static ApiResult Success(object data)
{
return new ApiResult
{
Code = 0,
Message = "ok",
Data = data
};
}
public static ApiResult Error(string msg)
{
return new ApiResult
{
Code = 1,
Message = msg
};
}
}
Unified JSON response format:
{
"code":0,
"message":"ok",
"data":{}
}
Advantages:
- Unified front-end parsing
- Simplified error handling
- Standardized API structure
V. Service Layer (Business Logic)
Business logic should be separated from HTTP processing.
Create:
ProductService.cs
public class ProductService
{
public List<object> GetProducts()
{
return new List<object>
{
new { id = 1, name = "Apple", price = 10 },
new { id = 2, name = "Orange", price = 8 }
};
}
public object GetProduct(string id)
{
return new { id = id, name = "Demo Product", price = 100 };
}
}
Responsibilities:
- Controller → calls Service
- Service → processes business logic
Benefits:
- Reusable business logic
- Simpler Controllers
- Easier maintenance and expansion
VI. Controller Layer (API Entry)
The Controller is responsible for:
- Receiving HTTP requests
- Reading parameters
- Calling Services
- Returning JSON
Create:
ProductController.cs
public class ProductController
{
private ProductService service = new ProductService();
public async Task List(HttpListenerRequest request, HttpListenerResponse response)
{
var data = service.GetProducts();
var result = ApiResult.Success(data);
string json = JsonSerializer.Serialize(result);
response.ContentType = "application/json";
await response.WriteAsync(json);
}
public async Task Detail(HttpListenerRequest request, HttpListenerResponse response)
{
string id = request.QueryString["id"];
var data = service.GetProduct(id);
var result = ApiResult.Success(data);
string json = JsonSerializer.Serialize(result);
response.ContentType = "application/json";
await response.WriteAsync(json);
}
}
The responsibilities of the Controller are very clear:
HTTP Request
↓
Read Parameters
↓
Call Service
↓
Return JSON
VII. Unified Route Registration
Next, create the server startup class:
PicoServerHost.cs
public class PicoServerHost
{
private readonly WebAPIServer api = new WebAPIServer();
public PicoServerHost()
{
RegisterRoutes();
api.StartServer();
}
private void RegisterRoutes()
{
var product = new ProductController();
api.AddRoute("/api/product/list", product.List);
api.AddRoute("/api/product/detail", product.Detail);
}
}
The complete call flow becomes:
HTTP Request
↓
PicoServer Router
↓
Controller
↓
Service
↓
Data / Device
This structure is already very close to a lightweight web framework.
VIII. Unified JSON Output Utility
To make Controllers more concise, we can encapsulate a utility method.
public static class HttpHelper
{
public static async Task WriteJson(HttpListenerResponse response, object obj)
{
string json = JsonSerializer.Serialize(obj);
await response.WriteAsync(json, contentType: "application/json");
}
}
Then the Controller can be written as:
await HttpHelper.WriteJson(response, ApiResult.Success(data));
The code becomes much cleaner.
IX. Unified Exception Handling
In an API system, it is recommended to capture exceptions uniformly:
try
{
var data = service.GetProducts();
await HttpHelper.WriteJson(response, ApiResult.Success(data));
}
catch(Exception ex)
{
await HttpHelper.WriteJson(response, ApiResult.Error(ex.Message));
}
This ensures that:
- APIs always return JSON
- No HTML error pages will appear
- Frontends can handle errors uniformly
Finally, remove the class PicoAdmin from MauiProgram.cs, and change the initialization new PicoAdmin() to:
var picoAdmin = new PicoServerHost(); // Instantiate PicoServerHost to start PicoServer
var picoAdmin = new PicoServerHost();
X. Complete Upgraded Architecture
After the transformation in this article, the overall system structure is as follows:
Browser / WebView
↓
HTTP
↓
PicoServer
↓
Router
↓
Controller
↓
Service
↓
Data / Device
Advantages:
- Clear API structure
- Modular architecture
- Easy to extend
- Easy to maintain
At this point, we have built a:
lightweight embedded REST API framework
XI. Further Expansion Capabilities
Based on the current architecture, many additional capabilities can be extended, such as:
- Middleware system
- Logging
- Authentication
- Request filtering
- CORS
- Token-based login system
/api/login/api/user/info
- Web Admin backend
Integrate with frontend frameworks:
- Vue
- React
- Admin Dashboard
To implement a complete management system.
XII. Summary of This Article
In this article, we completed the architecture upgrade of the PicoServer API.
New capabilities added:
- API layered architecture
- Controller/Service architecture
- Unified JSON response
- Unified route registration
- Exception handling mechanism
At this point, our MAUI application has been equipped with:
a scalable local REST API service framework
This also serves as the foundation for building the Web Admin backend in subsequent steps.
Next Article Preview
The next article will cover a very critical part:
Practical Combat of MAUI Embedded Web Architecture (IV)
Static File Hosting and Frontend Framework Integration
We will implement:
- Hosting HTML/CSS/JS files
- Hosting Vue/React build artifacts
- Setting default homepage
- Building a true local Web Admin system
The final architecture will be:
Browser
↓
localhost:8090
↓
Web Admin UI
↓
PicoServer API
↓
MAUI Local Logic
Top comments (0)