搜索结果

×

搜索结果将在这里显示。

🍉使用示例(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.路由和参数解析

路由有三种风格。

  1. 精准路由
  2. 星号路由
  3. 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 中间件采用责任链模式,按添加顺序执行。

二次开发示例参考:🛠️二次开发

发布时间: