🍉使用示例(VB.NET)
安装方式 参考 快速入门
默认端口8090,传入参数则监听指定端口,Windows下注意赋予管理员权限
推荐使用 PicoServer 最新版本,保障功能完整性
完整的示例代码,直接复制粘贴即可运行并验证
1.最简 WebAPI
Private ReadOnly MyAPI As New WebAPIServer '实例化PicoServer
Sub Main()
MyAPI.AddRoute("/", AddressOf Hello) '添加根路由映射
MyAPI.StartServer()
Console.WriteLine("http://127.0.0.1:8090")
Console.ReadKey()
MyAPI.StopServer() '停止服务
End Sub
'根路由映射的方法
Private Async Function Hello(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Await response.WriteAsync("Hello PicoServer")
End Function
相关方法:
MyAPI.AddRoute("/", AddressOf Hello) '路由映射,不限制请求方法
MyAPI.AddRoute("/", AddressOf Hello, "GET") '路由映射,限定为 GET 方法
MyAPI.StartServer '开启服务
MyAPI.StopServer '停止服务
response.WriteAsync("Hello PicoServer") '写入文本响应(UTF-8编码)
request.HttpMethod '获取请求方法,可根据此实现同一个路由对不同请求方法进行不同处理
2.路由和参数解析
路由有三种风格。
- 精准路由
- 星号路由
- RESTful 风格路由(文档后面)
Private ReadOnly MyAPI As New WebAPIServer
Sub Main()
' 注册精确路由(优先级高于通配符路由)
MyAPI.AddRoute("/api/user/query", AddressOf QueryUser, "GET")
MyAPI.AddRoute("/api/user/save", AddressOf SaveUser, "POST")
MyAPI.AddRoute("/api/user/json", AddressOf SaveUserJson, "POST")
' 注册星号通配符路由(每段 URL 仅支持一个 *,支持多段通配)
MyAPI.AddRoute("/api/*/posts", AddressOf HandleWildcardPost, "POST")
MyAPI.AddRoute("/api/*/user/*/detail", AddressOf HandleMultiWildcard, "GET")
' 启动服务
MyAPI.StartServer()
Console.WriteLine("服务已启动 http://127.0.0.1:8090")
Console.ReadKey()
MyAPI.StopServer()
End Sub
'处理 GET 查询参数请求
'路由:/api/user/query
Private Async Function QueryUser(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Dim name As String = request.GetQuery("name")
Dim age As Integer = request.GetQuery(Of Integer)("age")
Dim isVip As Boolean = request.GetQuery(Of Boolean)("isVip")
Dim output = $"{{
""code"": 1,
""msg"": ""参数解析成功"",
""data"": {{
""name"": ""{name}"",
""age"": {age},
""isVip"": {isVip}
}}
}}"
Await response.WriteAsync(output)
End Function
'处理 POST 表单数据请求
'路由:/api/user/save
'Content-Type: application/x-www-form-urlencoded
Private Async Function SaveUser(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Dim formData = request.ParseForm()
Dim userName As String = formData("userName")
Dim phone As String = formData("phone")
Await response.WriteAsync($"{{""code"":1, ""msg"":""表单保存成功"",""data"":{{""userName"":""{userName}"",""phone"":""{phone}""}}}}")
End Function
'处理 POST JSON 数据请求
'路由: /api/user/json
'Content-Type: application/json
Private Async Function SaveUserJson(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Dim bodyJson As String = Await request.ReadBodyAsStringAsync()
Await response.WriteAsync($"{{""code"":1, ""msg"":""JSON 保存成功"",""data"":{bodyJson}}}")
End Function
'处理单层星号通配符 POST 请求
'路由:POST /api/*/posts(匹配 /api/xxx/posts,* 为任意单段路径)
'内置防目录遍历,自动拦截 ../ 等非法字符
Private Async Function HandleWildcardPost(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Dim requestUrl As String = request.Url.AbsolutePath
Dim bodyJson As String = Await request.ReadBodyAsStringAsync()
Dim output = $"{{
""code"": 1,
""msg"": ""通配符路由匹配成功"",
""data"": {{
""requestUrl"": ""{requestUrl}"",
""receivedData"": {bodyJson}
}}
}}"
Await response.WriteAsync(output)
End Function
'处理多层星号通配符 GET 请求
' 路由:GET /api/*/user/*/detail(匹配 /api/xxx/user/yyy/detail,每段一个 *)
Private Async Function HandleMultiWildcard(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Dim requestUrl As String = request.Url.AbsolutePath
Dim output = $"{{""code"":1,""msg"":""多层通配符匹配成功"",""requestUrl"":""{requestUrl}""}}"
Await response.WriteAsync(output)
End Function
相关方法
request.GetQuery()'获取指定查询参数,不存在则返回nothing
request.GetQuery(Of T) '按类型返回指定查询参数,失败返回类型默认值
request.ParseForm() '获取表单数据
request.ReadBodyAsStringAsync() '获取 body 字符串
3.Cookie 增删改查
Sub Main()
MyAPI.AddRoute("/cookie/set", AddressOf SetCookie, "GET")
MyAPI.AddRoute("/cookie/get", AddressOf GetCookie, "GET")
MyAPI.AddRoute("/cookie/delete", AddressOf DeleteCookie, "GET")
MyAPI.AddRoute("/cookie/clear", AddressOf ClearCookies, "GET")
MyAPI.StartServer(8090)
Console.WriteLine("Cookie 测试服务已启动:http://127.0.0.1:8090")
Console.ReadKey()
MyAPI.StopServer()
End Sub
' 设置 Cookie(支持过期时间、路径、HttpOnly)
Private Async Function SetCookie(request As HttpListenerRequest, response As HttpListenerResponse) As Task
' 添加普通 Cookie(1小时过期)
response.AppendCookie("token", "pico_1234567", New CookieOptions With {
.Expires = DateTimeOffset.Now.AddHours(1),
.Path = "/",
.HttpOnly = True ' 防止前端 JS 读取,提升安全性
})
' 添加自定义路径 Cookie
response.AppendCookie("theme", "dark", New CookieOptions With {
.Expires = DateTimeOffset.Now.AddDays(7),
.Path = "/"
})
response.BuildCookie() '关键,多个Cookie需进行拼接
Await response.WriteAsync("{""code"":1, ""msg"":""Cookie 设置成功""}")
End Function
' 读取 Cookie
Private Async Function GetCookie(request As HttpListenerRequest, response As HttpListenerResponse) As Task
' 安全读取 Cookie(避免空引用)
Dim token As String = Nothing
request.TryGetCookieValue("token", token)
Dim theme As String = Nothing
request.TryGetCookieValue("theme", theme)
Await response.WriteAsync($"{{""code"":1, ""token"":""{token}"",""theme"":""{theme}""}}")
End Function
' 删除指定 Cookie
Private Async Function DeleteCookie(request As HttpListenerRequest, response As HttpListenerResponse) As Task
response.DeleteCookie("token", New CookieOptions With {.Path = "/"})
Await response.WriteAsync("{""code"":1, ""msg"":""Token Cookie 删除成功""}")
End Function
' 批量清理所有 Cookie
Private Async Function ClearCookies(request As HttpListenerRequest, response As HttpListenerResponse) As Task
response.ClearCookies()
Await response.WriteAsync("{""code"":1, ""msg"":""所有 Cookie 已清理""}")
End Function
4.RESTful 风格路由
Private ReadOnly MyAPI As New WebAPIServer()
Sub Main()
MyAPI.AddRoute("/users", AddressOf Users)
MyAPI.StartServer()
Console.WriteLine("服务已启动:http://127.0.0.1:8090")
Console.ReadKey()
MyAPI.StopServer()
End Sub
Private Async Function Users(request As HttpListenerRequest, response As HttpListenerResponse) As Task
response.ContentType = GetContentType(".json")
Select Case request.HttpMethod
Case "GET"
Await response.WriteAsync("{""code"":1,""msg"":""获取用户成功"",""data"":{""username"":""PicoServer""}}")
Case "POST"
response.StatusCode = 201
Await response.WriteAsync("{""code"":1,""msg"":""创建用户成功"",""data"":{""userId"":""123456""}}")
Case Else
response.StatusCode = 405
Await response.WriteAsync("{""code"":0,""msg"":""方法不允许""}")
End Select
End Function
相关方法
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架构的网站应用
Friend ReadOnly MyAPI As New WebAPIServer
Sub Main()
'添加静态文件服务, wwwroot 目录 /api/ 路径 同时兼容 Web 前端页面和 WebAPI 接口
MyAPI.AddStaticFiles("/", "wwwroot", "/api/")
MyAPI.AddCors() '允许跨域
MyAPI.StartServer()
'保持程序作为服务运行,兼容windows和linux
Console.WriteLine("服务器已启动,按Ctrl+C退出...")
Thread.Sleep(Timeout.Infinite)
End Sub
相关函数
'添加静态文件服务。第一个参数为路由,第二个为服务端文件夹(相对/绝对路径),第三个为排除的API路径,专为B/S架构添加,用于同时兼容 Web 前端页面和 WebAPI 接口
MyAPI.AddStaticFiles("/", "wwwroot", "/api/")
MyAPI.AddCors() '启用跨域
6.跨域配置
解决前后端分离跨域限制,支持极简配置与自定义配置
MyAPI.AddCors() '启用跨域,默认允许所有来源/方法/请求头
MyAPI.AddCors("picoserver.cn") '支持指定跨域
'更多个性化跨域自定义跨域中间件即可。
7.文件上传/下载
Private ReadOnly MyAPI As New WebAPIServer()
Sub Main()
MyAPI.AddRoute("/file/download", AddressOf DownloadFile, "GET") ' 文件下载/预览
MyAPI.AddRoute("/file/upload", AddressOf UploadFile, "POST") ' 文件上传
MyAPI.StartServer()
Console.WriteLine("文件服务已启动:http://127.0.0.1:8090")
Console.ReadKey()
MyAPI.StopServer()
End Sub
' 文件下载/预览(asAttachment=false 预览,true 强制下载)
Private Async Function DownloadFile(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Dim TestFile As String = "D:\test.mp4"
If Not File.Exists(TestFile) Then
Await response.WriteAsync("{""code"":0, ""msg"":""文件不存在""}")
Return
End If
Await response.SendFileAsync(TestFile, True)
End Function
' 文件上传(带进度回调)
Private Async Function UploadFile(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Dim fname As String = request.Headers("filename") '从请求头中获取文件名(test112.mp4),示例为相对位置,中文文件名建议用BASE64编码或者URL编码
Dim isSuccess As Boolean = Await request.SaveFileAsync(fname, Sub(current, total)' 打印上传进度(百分比) Console.WriteLine($"上传进度:{ current * 100 / total}%")
End Sub)
If isSuccess Then
Await response.WriteAsync("{""code"":1, ""msg"":""文件上传成功""}")
Else
Await response.WriteAsync("{""code"":0, ""msg"":""文件上传失败""}")
End If
Console.WriteLine($"请求头中的文件名:{fname}")
End Function
相关函数
'发送文件,支持断点下载
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 MyAPI As New WebAPIServer()
Sub Main()
MyAPI.AddRoute("/stream/live", AddressOf LiveStream, "GET") ' 直播流推送
MyAPI.StartServer()
Console.WriteLine("流媒体服务已启动:http://127.0.0.1:8090")
Console.ReadKey()
MyAPI.StopServer()
End Sub
Private Async Function LiveStream(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Dim LiveFile As String = "D:\test.mp4" ' 直播源文件(可替换为实时流)
If Not File.Exists(LiveFile) Then
Await response.WriteAsync("{""code"":0, ""msg"":""直播源不存在""}")
Return
End If
' 打开文件流(FileShare.ReadWrite 允许文件被其他程序写入)
Using fs As New FileStream(LiveFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
Await response.SendStreamAsync(fs, GetContentType(".mp4"), True)
End Using
End Function
相关方法
response.SendStreamAsync(fs, GetContentType(".mp4"), True)'无缓存、Chunked 传输,适配实时流,各种流
9.简单 Token/JWT 鉴权
一旦添加鉴权,默认所有路由都需要鉴权,不需要鉴权的路由需要添加到路由白名单
白名单只针对精准路由(路径),不支持星号路由
路由白名单
MyAPI.RouteWhiteList '储存路由白名单的集合
MyAPI.RouteWhiteList.Add("/api/login") '添加接口到白名单,无需验证
简单token鉴权
Private ReadOnly MyAPI As New WebAPIServer()
Private _testToken As String = "PicoServer123" ' 测试用 Token
Sub Main()
MyAPI.AddRoute("/api/login", AddressOf Login, "POST")
MyAPI.AddRoute("/api/user/info", AddressOf GetUserInfo, "GET")
'配置白名单(放行登录接口)
MyAPI.RouteWhiteList.Add("/api/login")
MyAPI.AddSimpleTokenVerify(_testToken)
MyAPI.StartServer()
Console.WriteLine($"鉴权服务已启动,测试 Token:{_testToken}")
Console.ReadKey()
MyAPI.StopServer()
End Sub
' 登录接口(白名单,无需鉴权)
Private Async Function Login(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Await response.WriteAsync($"{{""code"":1, ""msg"":""登录成功"",""token"":""{_testToken}""}}")
End Function
' 用户信息接口(需鉴权,非白名单)
Private Async Function GetUserInfo(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Dim token As String = request.GetToken()
Await response.WriteAsync($"{{""code"":1, ""msg"":""获取信息成功"",""token"":{token}}}")
End Function
相关方法
MyAPI.AddSimpleTokenVerify(testToken) '添加简单 token 验证中间件,参数为token
request.GetToken() '获取请求头中的token值
JWT鉴权
Private ReadOnly MyAPI As New WebAPIServer()
Private _testToken As String ' 测试用 Token
Sub Main()
' 1. 注册路由(先注册,后配置鉴权)
MyAPI.AddRoute("/api/login", AddressOf Login, "POST") ' 白名单路由(无需鉴权)
MyAPI.AddRoute("/api/user/info", AddressOf 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()
End Sub
' 简化版:生成测试 JWT Token
Private Sub GenerateTestToken()
' 过期时间:当前+1小时(10位时间戳)
Dim exp As Long = MyAPI.GetTimeStamp10(3600)
' 直接使用字符串插值构造载荷,简化代码
Dim payload As String = $"{{""username"":""admin"",""role"":""super"",""exp"":{exp}}}"
' 生成 Token
_testToken = MyAPI.Jwt.GenerateToken(payload)
End Sub
' 登录接口(白名单,无需鉴权)
Private Async Function Login(request As HttpListenerRequest, response As HttpListenerResponse) As Task
' 模拟登录验证(实际场景替换为账号密码校验)
Await response.WriteAsync($"{{""code"":1, ""msg"":""登录成功"",""token"":""{_testToken}""}}")
End Function
' 用户信息接口(需鉴权,非白名单)
Private Async Function GetUserInfo(request As HttpListenerRequest, response As HttpListenerResponse) As Task
' 获取请求头中的 Token 并解析载荷
Dim token As String = request.GetToken()
Dim payload As String = MyAPI.Jwt.DecodePayload(token)
Await response.WriteAsync($"{{""code"":1, ""msg"":""获取信息成功"",""userInfo"":{payload}}}")
End Function
相关方法
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 MyAPI As New WebAPIServer()
Sub Main()
MyAPI.AddRoute("/notify", AddressOf LongConnectionPush, "GET") ' 长连接推送
MyAPI.StartServer(8090)
Console.WriteLine("长连接服务已启动:http://127.0.0.1:8090/notify")
Console.ReadKey()
MyAPI.StopServer()
End Sub
' 长连接消息推送(模拟设备报警)
Private Async Function LongConnectionPush(request As HttpListenerRequest, response As HttpListenerResponse) As Task
Try
' 循环推送消息(实际场景替换为业务事件触发)
For i As Integer = 0 To 29
' 推送报警消息(低内存)
Await response.WriteChunkAsync($"设备报警 {i}:温度异常 {DateTime.Now:HH:mm:ss}" & vbCrLf)
' 模拟 1 秒推送一次
Await Task.Delay(1000)
Next
' 推送结束,发送结束标识
Await response.WriteChunkAsync("推送结束")
Finally
' 必须关闭响应
response.Close()
End Try
End Function
相关方法
response.WriteChunkAsync("推送结束") '推送字符串
response.Close() '关闭响应,释放资源 【WriteChunkAsync下必须手动关闭,避免资源泄露】
11.WebSocket 通信
-
场景目标:实现 WebSocket 双向交互,适配实时聊天、设备指令交互等场景
-
核心要点:服务端(enableWebSocket 启用、事件订阅、在线客户端管理、广播消息)、客户端(WebSocketClient 初始化、事件订阅、消息发送)
-
运行测试:服务端启动、客户端连接、双向消息交互、广播消息接收
WebSocket 服务端可以和 WebAPI 同时存在,且共用端口,共用地址
WebSocket 服务端
Private ReadOnly MyAPI As New WebAPIServer()
Sub Main()
MyAPI.enableWebSocket = True
MyAPI.WsOnConnectionChanged = AddressOf WsConnectChanged
MyAPI.WsOnMessage = AddressOf OnMessageReceived
MyAPI.StartServer()
Console.WriteLine("PicoServer WebSocket:http://127.0.0.1:8090")
Console.ReadKey()
MyAPI.StopServer()
End Sub
Private Async Function WsConnectChanged(clientId As String, connected As Boolean) As Task
Await MyAPI.WsBroadcastAsync($"{clientId} {connected}")
End Function
Private Async Function OnMessageReceived(clientId As String, message As String, reply As Func(Of String, Task)) As Task
Await reply("收到!")
Dim clients = MyAPI.WsGetOnlineClients
For Each client In clients
Await MyAPI.WsSendToClientAsync(client, $"{clientId}说:{message}")
Next
End Function
相关方法
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 wsClient As New WebSocketClient("wss://echo.websocket.org/")
Sub Main()
' 订阅核心事件
AddHandler wsClient.OnConnected, AddressOf OnConnected ' 连接成功
AddHandler wsClient.OnMessageReceived, AddressOf OnMessageReceived ' 接收消息
AddHandler wsClient.OnDisconnected, AddressOf OnDisconnected ' 连接断开
AddHandler wsClient.OnError, AddressOf OnError ' 异常触发
wsClient.StartConnect()
Console.ReadKey()
wsClient.StopConnect()
End Sub
Private Sub OnConnected(sender As Object, e As EventArgs)
Console.WriteLine("连接成功!")
End Sub
Private Sub OnMessageReceived(sender As Object, message As String)
Console.WriteLine($"收到消息: {message}")
wsClient.SendMessageAsync($"hi: {Date.Now:T}")
End Sub
Private Sub OnDisconnected(sender As Object, e As EventArgs)
Console.WriteLine("连接已断开!")
End Sub
Private Sub OnError(sender As Object, e As WebSocketErrorEventArgs)
Console.WriteLine($"错误: {e.ErrorCode}, {e.ErrorMessage}")
End Sub
相关方法
Private wsClient As New WebSocketClient() '实例化WebSocket客户端
Private wsClient As New WebSocketClient("wss://echo.websocket.org/") '实例化WebSocket服务端,支持ws和wss,默认5秒超时
Private wsClient As 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 中间件采用责任链模式,按添加顺序执行。
二次开发示例参考:🛠️二次开发