🍉使用示例(C#)
安装方式 参考 快速入门
默认端口8090,传入参数则监听指定端口,Windows下注意赋予管理员权限
推荐使用 PicoServer 最新版本,保障功能完整性
完整的示例代码,直接复制粘贴即可运行并验证
1.最简 WebAPI
private readonly WebAPIServer MyAPI = new WebAPIServer(); //实例化PicoServer
static void Main()
{
MyAPI.AddRoute("/", Hello); //添加根路由映射
MyAPI.StartServer();
Console.WriteLine("http://127.0.0.1:8090");
Console.ReadKey();
MyAPI.StopServer(); //停止服务
}
//根路由映射的方法
private async Task Hello(HttpListenerRequest request, HttpListenerResponse response)
{
await response.WriteAsync("Hello PicoServer");
}
相关方法:
MyAPI.AddRoute("/", Hello); //路由映射,不限制请求方法
MyAPI.AddRoute("/", Hello, "GET"); //路由映射,限定为 GET 方法
MyAPI.StartServer(); //开启服务
MyAPI.StopServer(); //停止服务
response.WriteAsync("Hello PicoServer"); //写入文本响应(UTF-8编码)
request.HttpMethod; //获取请求方法,可根据此实现同一个路由对不同请求方法进行不同处理
2.路由和参数解析
路由有三种风格。
- 精准路由
- 星号路由
- RESTful 风格路由(文档后面)
private static readonly WebAPIServer MyAPI = new WebAPIServer();
static void Main()
{
// 注册精确路由(优先级高于通配符路由)
MyAPI.AddRoute("/api/user/query", QueryUser, "GET");
MyAPI.AddRoute("/api/user/save", SaveUser, "POST");
MyAPI.AddRoute("/api/user/json", SaveUserJson, "POST");
// 注册星号通配符路由(每段 URL 仅支持一个 *,支持多段通配)
MyAPI.AddRoute("/api/*/posts", HandleWildcardPost, "POST");
MyAPI.AddRoute("/api/*/user/*/detail", HandleMultiWildcard, "GET");
// 启动服务
MyAPI.StartServer();
Console.WriteLine("服务已启动 http://127.0.0.1:8090");
Console.ReadKey();
MyAPI.StopServer();
}
//处理 GET 查询参数请求
//路由:/api/user/query
private static async Task QueryUser(HttpListenerRequest request, HttpListenerResponse response)
{
string name = request.GetQuery("name");
int age = request.GetQuery<int>("age");
bool isVip = request.GetQuery<bool>("isVip");
string output = $@"{{
""code"": 1,
""msg"": ""参数解析成功"",
""data"": {{
""name"": ""{name}"",
""age"": {age},
""isVip"": {isVip}
}}
}}";
await response.WriteAsync(output);
}
//处理 POST 表单数据请求
//路由:/api/user/save
//Content-Type: application/x-www-form-urlencoded
private static async Task SaveUser(HttpListenerRequest request, HttpListenerResponse response)
{
var formData = request.ParseForm();
string userName = formData["userName"];
string phone = formData["phone"];
await response.WriteAsync($"{{""code"":1, ""msg"":""表单保存成功"",""data"":{{""userName"":""{userName}"",""phone"":""{phone}""}}}}");
}
//处理 POST JSON 数据请求
//路由: /api/user/json
//Content-Type: application/json
private static async Task SaveUserJson(HttpListenerRequest request, HttpListenerResponse response)
{
string bodyJson = await request.ReadBodyAsStringAsync();
await response.WriteAsync($"{{""code"":1, ""msg"":""JSON 保存成功"",""data"":{bodyJson}}}");
}
//处理单层星号通配符 POST 请求
//路由:POST /api/*/posts(匹配 /api/xxx/posts,* 为任意单段路径)
//内置防目录遍历,自动拦截 ../ 等非法字符
private static async Task HandleWildcardPost(HttpListenerRequest request, HttpListenerResponse response)
{
string requestUrl = request.Url.AbsolutePath;
string bodyJson = await request.ReadBodyAsStringAsync();
string output = $@"{{
""code"": 1,
""msg"": ""通配符路由匹配成功"",
""data"": {{
""requestUrl"": ""{requestUrl}"",
""receivedData"": {bodyJson}
}}
}}";
await response.WriteAsync(output);
}
//处理多层星号通配符 GET 请求
// 路由:GET /api/*/user/*/detail(匹配 /api/xxx/user/yyy/detail,每段一个 *)
private static async Task HandleMultiWildcard(HttpListenerRequest request, HttpListenerResponse response)
{
string requestUrl = request.Url.AbsolutePath;
string output = $"{{""code"":1,""msg"":""多层通配符匹配成功"",""requestUrl"":""{requestUrl}""}}";
await response.WriteAsync(output);
}
相关方法
request.GetQuery();//获取指定查询参数,不存在则返回null
request.GetQuery<T>(); //按类型返回指定查询参数,失败返回类型默认值
request.ParseForm(); //获取表单数据
request.ReadBodyAsStringAsync(); //获取 body 字符串
3.Cookie 增删改查
static void Main()
{
MyAPI.AddRoute("/cookie/set", SetCookie, "GET");
MyAPI.AddRoute("/cookie/get", GetCookie, "GET");
MyAPI.AddRoute("/cookie/delete", DeleteCookie, "GET");
MyAPI.AddRoute("/cookie/clear", ClearCookies, "GET");
MyAPI.StartServer(8090);
Console.WriteLine("Cookie 测试服务已启动:http://127.0.0.1:8090");
Console.ReadKey();
MyAPI.StopServer();
}
// 设置 Cookie(支持过期时间、路径、HttpOnly)
private async Task SetCookie(HttpListenerRequest request, HttpListenerResponse response)
{
// 添加普通 Cookie(1小时过期)
response.AppendCookie("token", "pico_1234567", new CookieOptions
{
Expires = DateTimeOffset.Now.AddHours(1),
Path = "/",
HttpOnly = true // 防止前端 JS 读取,提升安全性
});
// 添加自定义路径 Cookie
response.AppendCookie("theme", "dark", new CookieOptions
{
Expires = DateTimeOffset.Now.AddDays(7),
Path = "/"
});
response.BuildCookie(); //关键,多个Cookie需进行拼接
await response.WriteAsync(@"{""code"":1, ""msg"":""Cookie 设置成功""}");
}
// 读取 Cookie
private async Task GetCookie(HttpListenerRequest request, HttpListenerResponse response)
{
// 安全读取 Cookie(避免空引用)
string token = null;
request.TryGetCookieValue("token", out token);
string theme = null;
request.TryGetCookieValue("theme", out theme);
await response.WriteAsync($@"{{""code"":1, ""token"":""{token}"",""theme"":""{theme}""}}");
}
// 删除指定 Cookie
private async Task DeleteCookie(HttpListenerRequest request, HttpListenerResponse response)
{
response.DeleteCookie("token", new CookieOptions { Path = "/" });
await response.WriteAsync(@"{""code"":1, ""msg"":""Token Cookie 删除成功""}");
}
// 批量清理所有 Cookie
private async Task ClearCookies(HttpListenerRequest request, HttpListenerResponse response)
{
response.ClearCookies();
await response.WriteAsync(@"{""code"":1, ""msg"":""所有 Cookie 已清理""}");
}
4.RESTful 风格路由
private readonly WebAPIServer MyAPI = new WebAPIServer();
static void Main()
{
MyAPI.AddRoute("/users", Users);
MyAPI.StartServer();
Console.WriteLine("服务已启动:http://127.0.0.1:8090");
Console.ReadKey();
MyAPI.StopServer();
}
private async Task Users(HttpListenerRequest request, HttpListenerResponse response)
{
response.ContentType = GetContentType(".json");
switch (request.HttpMethod)
{
case "GET":
await response.WriteAsync(@"{""code"":1,""msg"":""获取用户成功"",""data"":{""username"":""PicoServer""}}");
break;
case "POST":
response.StatusCode = 201;
await response.WriteAsync(@"{""code"":1,""msg"":""创建用户成功"",""data"":{""userId"":""123456""}}");
break;
default:
response.StatusCode = 405;
await response.WriteAsync(@"{""code"":0,""msg"":""方法不允许""}");
break;
}
}
相关方法
GetContentType(".html"); //根据文件扩展名获取 MIME 类型:text/html;charset=UTF-8
GetContentType 为跨平台通用方法(Windows/Linux/Docker 结果一致),可根据文件扩展名获取标准化 MIME 类型,支持的扩展名有:.html/.htm、.css、.js、.json、.txt、.xml、.jpg/.jpeg、.png、.gif、.webp、.svg/.svgz、.ico、.mp4、.webm、.mp3、.wav、.m3u8、.ts、.woff2、.woff、.ttf、.otf、.pdf、.zip、.rar、.7z、.apk;文本类类型默认带 UTF-8 编码,未知类型返回 application/octet-stream。
5.静态文件托管
静态文件(HTML/CSS/JS/ 图片 / 视频)托管配置,适配前端页面直接访问、静态资源服务等场景。如B/S架构的网站应用
internal readonly WebAPIServer MyAPI = new WebAPIServer();
static void Main()
{
//添加静态文件服务, wwwroot 目录 /api/ 路径 同时兼容 Web 前端页面和 WebAPI 接口
MyAPI.AddStaticFiles("/", "wwwroot", "/api/");
MyAPI.AddCors(); //允许跨域
MyAPI.StartServer();
//保持程序作为服务运行,兼容windows和linux
Console.WriteLine("服务器已启动,按Ctrl+C退出...");
Thread.Sleep(Timeout.Infinite);
}
相关函数
//添加静态文件服务。第一个参数为路由,第二个为服务端文件夹(相对/绝对路径),第三个为排除的API路径,专为B/S架构添加,用于同时兼容 Web 前端页面和 WebAPI 接口
MyAPI.AddStaticFiles("/", "wwwroot", "/api/");
MyAPI.AddCors(); //启用跨域
6.跨域配置
解决前后端分离跨域限制,支持极简配置与自定义配置
MyAPI.AddCors(); //启用跨域,默认允许所有来源/方法/请求头
MyAPI.AddCors("picoserver.cn"); //支持指定跨域
//更多个性化跨域自定义跨域中间件即可。
7.文件上传/下载
private readonly WebAPIServer MyAPI = new WebAPIServer();
static void Main()
{
MyAPI.AddRoute("/file/download", DownloadFile, "GET"); // 文件下载/预览
MyAPI.AddRoute("/file/upload", UploadFile, "POST"); // 文件上传
MyAPI.StartServer();
Console.WriteLine("文件服务已启动:http://127.0.0.1:8090");
Console.ReadKey();
MyAPI.StopServer();
}
// 文件下载/预览(asAttachment=false 预览,true 强制下载)
private async Task DownloadFile(HttpListenerRequest request, HttpListenerResponse response)
{
string TestFile = "D:\\test.mp4";
if (!File.Exists(TestFile))
{
await response.WriteAsync(@"{""code"":0, ""msg"":""文件不存在""}");
return;
}
await response.SendFileAsync(TestFile, true);
}
// 文件上传(带进度回调)
private async Task UploadFile(HttpListenerRequest request, HttpListenerResponse response)
{
string fname = request.Headers["filename"]; //从请求头中获取文件名(test112.mp4),示例为相对位置,中文文件名建议用BASE64编码或者URL编码
bool isSuccess = await request.SaveFileAsync(fname, (current, total) =>
{
// 打印上传进度(百分比)
Console.WriteLine($"上传进度:{ current * 100 / total}%");
});
if (isSuccess)
{
await response.WriteAsync(@"{""code"":1, ""msg"":""文件上传成功""}");
}
else
{
await response.WriteAsync(@"{""code"":0, ""msg"":""文件上传失败""}");
}
Console.WriteLine($"请求头中的文件名:{fname}");
}
相关函数
//发送文件,支持断点下载
response.SendFileAsync();//流式发送,大文件低内存消耗,根据扩展名自动添加文件类型
response.SendFileAsync(filePath);//支持文档/视频等直接预览
response.SendFileAsync(filePath,true);//强制下载
response.SendFileAsync(Mp4Path,false,request);//播放视频,支持拖动播放
//接受文件上传,支持断点续传
request.SaveFileAsync();//流式保存,大文件低内存消耗
request.SaveFileAsync(filePath);//保存文件到指定路径(相对/绝对皆可),需要包含文件名
request.SaveFileAsync(filePath,onProgress);//保存文件到指定路径,支持回调进度。
request.Headers["filename"]; //举例:从请求头中获取文件名,生产中应进行非空判断
8.流媒体/直播流推送
private readonly WebAPIServer MyAPI = new WebAPIServer();
static void Main()
{
MyAPI.AddRoute("/stream/live", LiveStream, "GET"); // 直播流推送
MyAPI.StartServer();
Console.WriteLine("流媒体服务已启动:http://127.0.0.1:8090");
Console.ReadKey();
MyAPI.StopServer();
}
private async Task LiveStream(HttpListenerRequest request, HttpListenerResponse response)
{
string LiveFile = "D:\\test.mp4"; // 直播源文件(可替换为实时流)
if (!File.Exists(LiveFile))
{
await response.WriteAsync(@"{""code"":0, ""msg"":""直播源不存在""}");
return;
}
// 打开文件流(FileShare.ReadWrite 允许文件被其他程序写入)
using (FileStream fs = new FileStream(LiveFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
await response.SendStreamAsync(fs, GetContentType(".mp4"), true);
}
}
相关方法
response.SendStreamAsync(fs, GetContentType(".mp4"), true);//无缓存、Chunked 传输,适配实时流,各种流
9.简单 Token/JWT 鉴权
一旦添加鉴权,默认所有路由都需要鉴权,不需要鉴权的路由需要添加到路由白名单
白名单只针对精准路由(路径),不支持星号路由
路由白名单
MyAPI.RouteWhiteList; //储存路由白名单的集合
MyAPI.RouteWhiteList.Add("/api/login"); //添加接口到白名单,无需验证
简单token鉴权
private readonly WebAPIServer MyAPI = new WebAPIServer();
private string _testToken = "PicoServer123"; // 测试用 Token
static void Main()
{
MyAPI.AddRoute("/api/login", Login, "POST");
MyAPI.AddRoute("/api/user/info", GetUserInfo, "GET");
//配置白名单(放行登录接口)
MyAPI.RouteWhiteList.Add("/api/login");
MyAPI.AddSimpleTokenVerify(_testToken);
MyAPI.StartServer();
Console.WriteLine($"鉴权服务已启动,测试 Token:{_testToken}");
Console.ReadKey();
MyAPI.StopServer();
}
// 登录接口(白名单,无需鉴权)
private async Task Login(HttpListenerRequest request, HttpListenerResponse response)
{
await response.WriteAsync($@"{{""code"":1, ""msg"":""登录成功"",""token"":""{_testToken}""}}");
}
// 用户信息接口(需鉴权,非白名单)
private async Task GetUserInfo(HttpListenerRequest request, HttpListenerResponse response)
{
string token = request.GetToken();
await response.WriteAsync($@"{{""code"":1, ""msg"":""获取信息成功"",""token"":{token}}}");
}
相关方法
MyAPI.AddSimpleTokenVerify(testToken); //添加简单 token 验证中间件,参数为token
request.GetToken(); //获取请求头中的token值
JWT鉴权
private readonly WebAPIServer MyAPI = new WebAPIServer();
private string _testToken; // 测试用 Token
static void Main()
{
// 1. 注册路由(先注册,后配置鉴权)
MyAPI.AddRoute("/api/login", Login, "POST"); // 白名单路由(无需鉴权)
MyAPI.AddRoute("/api/user/info", GetUserInfo, "GET"); // 需鉴权路由
// 2. 配置白名单(放行登录接口)
MyAPI.RouteWhiteList.Add("/api/login");
// 3. 启用 JWT 鉴权(密钥需与生成 Token 时一致)
MyAPI.AddJwtTokenVerify("pico_secret_779");
// 4. 生成测试 Token(模拟登录成功)
GenerateTestToken();
MyAPI.StartServer();
Console.WriteLine($"鉴权服务已启动,测试 Token:{_testToken}");
Console.ReadKey();
MyAPI.StopServer();
}
// 简化版:生成测试 JWT Token
private void GenerateTestToken()
{
// 过期时间:当前+1小时(10位时间戳)
long exp = MyAPI.GetTimeStamp10(3600);
// 直接使用字符串插值构造载荷,简化代码
string payload = $@"{{""username"":""admin"",""role"":""super"",""exp"":{exp}}}";
// 生成 Token
_testToken = MyAPI.Jwt.GenerateToken(payload);
}
// 登录接口(白名单,无需鉴权)
private async Task Login(HttpListenerRequest request, HttpListenerResponse response)
{
// 模拟登录验证(实际场景替换为账号密码校验)
await response.WriteAsync($@"{{""code"":1, ""msg"":""登录成功"",""token"":""{_testToken}""}}");
}
// 用户信息接口(需鉴权,非白名单)
private async Task GetUserInfo(HttpListenerRequest request, HttpListenerResponse response)
{
// 获取请求头中的 Token 并解析载荷
string token = request.GetToken();
string payload = MyAPI.Jwt.DecodePayload(token);
await response.WriteAsync($@"{{""code"":1, ""msg"":""获取信息成功"",""userInfo"":{payload}}}");
}
相关方法
MyAPI.AddJwtTokenVerify("pico_secret_779");//添加 JWT 鉴权中间件,参数为HS256加密密钥
request.GetToken(); //获取请求头中的token值
MyAPI.Jwt.DecodePayload(token);//解码 JWT 负载为字符串
MyAPI.Jwt.GenerateToken(payload); //创建 JWT token 参数为负载,用于储存信息
MyAPI.GetTimeStamp10(3600); // 获取10位时间戳,参数:需要追加的秒数,不传入则返回当前时间戳
MyAPI.GetTimeStamp13(); // 获取13位时间戳,参数:需要追加的毫秒数,不传入则返回当前时间戳
10.长连接消息推送
private readonly WebAPIServer MyAPI = new WebAPIServer();
static void Main()
{
MyAPI.AddRoute("/notify", LongConnectionPush, "GET"); // 长连接推送
MyAPI.StartServer(8090);
Console.WriteLine("长连接服务已启动:http://127.0.0.1:8090/notify");
Console.ReadKey();
MyAPI.StopServer();
}
// 长连接消息推送(模拟设备报警)
private async Task LongConnectionPush(HttpListenerRequest request, HttpListenerResponse response)
{
try
{
// 循环推送消息(实际场景替换为业务事件触发)
for (int i = 0; i <= 29; i++)
{
// 推送报警消息(低内存)
await response.WriteChunkAsync($"设备报警 {i}:温度异常 {DateTime.Now:HH:mm:ss}" + "\r\n");
// 模拟 1 秒推送一次
await Task.Delay(1000);
}
// 推送结束,发送结束标识
await response.WriteChunkAsync("推送结束");
}
finally
{
// 必须关闭响应
response.Close();
}
}
相关方法
response.WriteChunkAsync("推送结束"); //推送字符串
response.Close(); //关闭响应,释放资源 【WriteChunkAsync下必须手动关闭,避免资源泄露】
11.WebSocket 通信
-
场景目标:实现 WebSocket 双向交互,适配实时聊天、设备指令交互等场景
-
核心要点:服务端(enableWebSocket 启用、事件订阅、在线客户端管理、广播消息)、客户端(WebSocketClient 初始化、事件订阅、消息发送)
-
运行测试:服务端启动、客户端连接、双向消息交互、广播消息接收
WebSocket 服务端可以和 WebAPI 同时存在,且共用端口,共用地址
WebSocket 服务端
private readonly WebAPIServer MyAPI = new WebAPIServer();
static void Main()
{
MyAPI.enableWebSocket = true;
MyAPI.WsOnConnectionChanged = WsConnectChanged;
MyAPI.WsOnMessage = OnMessageReceived;
MyAPI.StartServer();
Console.WriteLine("PicoServer WebSocket:http://127.0.0.1:8090");
Console.ReadKey();
MyAPI.StopServer();
}
private async Task WsConnectChanged(string clientId, bool connected)
{
await MyAPI.WsBroadcastAsync($"{clientId} {connected}");
}
private async Task OnMessageReceived(string clientId, string message, Func<string, Task> reply)
{
await reply("收到!");
var clients = MyAPI.WsGetOnlineClients;
foreach (var client in clients)
{
await MyAPI.WsSendToClientAsync(client, $"{clientId}说:{message}");
}
}
相关方法
MyAPI.enableWebSocket = true; //启用WebSocket支持
MyAPI.WsOnConnectionChanged; // 事件:WebSocket客户端连接状态发生变化
MyAPI.WsOnMessage; //事件:收到WebSocket客户端发送来的消息
MyAPI.WsBroadcastAsync(); //对所有在线客户端广播消息
MyAPI.WsGetOnlineClients; //获取在线客户端列表
MyAPI.WsSendToClientAsync(client,message); //给指定客户端发送消息
MyAPI.WsEnableHeartbeat = true; //启用 WebSocket 服务端心跳检测,默认false
MyAPI.WsHeartbeatTimeout = 60; //设置 WebSocket 心跳时间,默认30秒
MyAPI.WsMaxConnections = 200; //设置 WebSocket 最大连接数,默认100
MyAPI.WsPingString = "hi"; //设置 WebSocket 的ping消息,默认"ping",不区分大小写
WebSocket 客户端
private WebSocketClient wsClient = new WebSocketClient("wss://echo.websocket.org/");
static void Main()
{
// 订阅核心事件
wsClient.OnConnected += OnConnected; // 连接成功
wsClient.OnMessageReceived += OnMessageReceived; // 接收消息
wsClient.OnDisconnected += OnDisconnected; // 连接断开
wsClient.OnError += OnError; // 异常触发
wsClient.StartConnect();
Console.ReadKey();
wsClient.StopConnect();
}
private void OnConnected(object sender, EventArgs e)
{
Console.WriteLine("连接成功!");
}
private void OnMessageReceived(object sender, string message)
{
Console.WriteLine($"收到消息: {message}");
wsClient.SendMessageAsync($"hi: {DateTime.Now.ToShortTimeString()}");
}
private void OnDisconnected(object sender, EventArgs e)
{
Console.WriteLine("连接已断开!");
}
private void OnError(object sender, WebSocketErrorEventArgs e)
{
Console.WriteLine($"错误: {e.ErrorCode}, {e.ErrorMessage}");
}
相关方法
private WebSocketClient wsClient = new WebSocketClient(); //实例化WebSocket客户端
private WebSocketClient wsClient = new WebSocketClient("wss://echo.websocket.org/"); //实例化WebSocket服务端,支持ws和wss,默认5秒超时
private WebSocketClient wsClient = new WebSocketClient("wss://echo.websocket.org/",10); //自定义超时时间10秒
wsClient.StartConnect(); //连接服务端
wsClient.StopConnect(); //断开连接
wsClient.SendMessageAsync(message); //发送消息
wsClient.SendPingAsync(); //发送ping消息,默认“ping”
wsClient.SendPingAsync("hi"); //发送自定义ping消息
12.二次开发
PicoServer 开放了 中间件 ,可以借此进行二次开发、封装、集成解决方案等 。
利用 AddMiddleware() 可开发属于自己的中间件。比如:参数路由,限流,黑名单等等。
PicoServer 中间件采用责任链模式,按添加顺序执行。
二次开发示例参考:🛠️二次开发