更新记录
1.0.0(2026-07-26)
- 首次发布,支持 uni-app / uni-app x 的 Android 与 iOS App。
- 支持本地 HTTP 服务的创建、启动、停止、重启、销毁和状态查询。
- 支持动态路由、路径参数、查询参数、JSON / 文本请求与响应、自定义状态码和响应头。
- 支持静态目录挂载、目录索引、MIME 推断、可选目录列举和
Cache-Control。 - 支持文件响应、
Range/HEAD、multipart 文件上传和 urlencoded 表单。 - 支持 CORS、请求超时、连接数、请求体和上传大小限制。
- 默认仅监听本机回环地址,可显式开放到局域网。
- 提供生命周期事件、统一错误码和日志开关。
平台兼容性
uni-app(5.15)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| √ | √ | - | - | √ | - | √ | √ | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(5.15)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | √ | √ | - | - |
本地 HTTP 服务(hans-http-server)
面向 uni-app x App 的本地 HTTP/1.1 UTS 插件:可挂载静态站点、注册动态接口、接收上传,并为本地媒体访问提供 MIME、Range 与 HEAD 基础能力。本文以 uni-app x 的 .uvue / UTS 用法为主。服务默认只监听本机回环,需要时再显式开放到局域网。
它能做什么
- 在 App 内启动 / 停止一个 HTTP 服务,并获取可访问地址与端口。
- 动态路由:
GET/POST/PUT/PATCH/DELETE/OPTIONS/HEAD,支持:param路径参数。 - 响应类型:JSON、文本、文件流(支持
Range/HEAD)、空响应;可自定义响应头与状态码。 - 静态站点:挂载目录、目录索引、MIME 推断、可选目录列举、
Cache-Control、路径越界拦截。 - 媒体访问:
Range(206 Partial Content,支持区间与后缀区间)、HEAD以及 mp4 / HLS 常用 MIME;实际播放效果取决于平台播放器支持。 - 表单与上传:解析
application/x-www-form-urlencoded;将multipart/form-data文件写入受控目录并解析普通字段。 - CORS:可配置来源白名单,自动处理预检与实际响应。
- 生命周期事件、统一错误码、可开关的日志。
平台支持
| uni-app x 平台 | 支持状态 |
|---|---|
| Android App | 支持 |
| iOS App | 支持 |
| HarmonyOS App | 不支持 |
| Web / 小程序 | 不支持 |
兼容性说明:同一套公开 API 也可用于传统 uni-app Vue2 / Vue3 的 Android 与 iOS App;本文的代码示例统一采用 uni-app x 的 UTS 写法。nvue、Web、小程序和快应用不支持。
平台配置与权限
| 平台 | 配置要求 |
|---|---|
| Android | 插件自动声明 android.permission.INTERNET;最低 minSdkVersion 21 |
| iOS | 最低 deploymentTarget 12;使用 host: 'lan' 时,宿主 Info.plist 必须声明 NSLocalNetworkUsageDescription |
host: 'lan' 会让同一局域网内的其他设备访问服务。仅在业务确实需要时启用,并由宿主应用负责访问控制和敏感文件隔离。
安装与导入
从 DCloud 插件市场将插件导入 uni-app x 项目。在 .uvue 页面的 <script setup lang="uts"> 中,从插件根目录导入函数和公开类型,不要直接引用 utssdk 内部文件:
import {
createHttpServer,
getResourcePath,
HttpServer,
HttpRequest,
HttpServerInfo,
HttpServerEvent,
} from '@/uni_modules/hans-http-server'
快速开始
以下代码放在 .uvue 页面的 <script setup lang="uts"> 中:
import {
createHttpServer,
getResourcePath,
HttpServer,
HttpRequest,
HttpServerInfo,
HttpServerEvent,
} from '@/uni_modules/hans-http-server'
const server: HttpServer = createHttpServer({
host: 'loopback', // 默认仅本机回环;'lan' 显式开放到局域网
port: 0, // 0 = 由系统分配空闲端口
logEnabled: true,
})
// 挂载静态目录(必须在 start 之前注册)
server.mount('/assets', {
root: getResourcePath('/static'),
index: 'index.html',
})
// 动态接口
server.route('GET', '/api/status', (req: HttpRequest, responseId: string): void => {
server.respond(responseId, {
statusCode: 200,
bodyJson: { ok: true, time: Date.now() },
})
})
server.route('GET', '/api/users/:id', (req: HttpRequest, responseId: string): void => {
server.respond(responseId, {
statusCode: 200,
bodyJson: { id: req.params['id'], query: req.query },
})
})
server.route('POST', '/api/upload', (req: HttpRequest, responseId: string): void => {
// multipart 文件已落盘;form 为普通字段;files 为受控路径列表
server.respond(responseId, {
statusCode: 200,
bodyJson: { files: req.files, fields: req.form },
})
})
server.route('GET', '/api/file', (req: HttpRequest, responseId: string): void => {
// 命中文件路由后,Range/HEAD 由插件自动处理
server.respond(responseId, {
statusCode: 200,
filePath: getResourcePath('/files/media.bin'),
contentType: 'application/octet-stream',
})
})
server.on('started', (event: HttpServerEvent): void => {
console.log('started', event.info)
})
server.on('error', (event: HttpServerEvent): void => {
console.error('server error', event.message)
})
async function startServer(): Promise<void> {
const info: HttpServerInfo = await server.start()
console.log(info.urls) // 例如 ['http://127.0.0.1:54321']
}
async function stopServer(): Promise<void> {
await server.stop() // 停止后可再次 start
}
async function destroyServer(): Promise<void> {
await server.destroy() // 永久释放,不可再 start
}
模块级 API
| API | 说明 |
|---|---|
createHttpServer(options?) |
创建一个尚未监听端口的服务实例 |
getResourcePath(path) |
将应用资源路径转换为当前平台原生文件系统可读取的路径 |
setLogEnabled(enabled) |
设置插件级日志开关(影响当前模块的所有服务实例) |
isLogEnabled() |
获取当前插件日志开关 |
公开类型
所有公开类型都从插件根目录导入。interface.uts 是 API 类型契约,不要在业务代码中直接引用该文件。
| 类型 | 用途 |
|---|---|
HttpServer |
服务实例及 mount、route、start、stop 等方法 |
HttpServerHost / HttpServerOptions |
监听模式与服务配置(包含 CORS 配置) |
StaticMountOptions |
静态目录挂载配置 |
HttpRequest / UploadedFile / HttpRouteHandler |
动态路由收到的请求、上传文件与回调签名 |
HttpRouteResponse / HttpHeader |
通过 respond() 提交的响应与响应头 |
HttpServerInfo / HttpServerStatus |
启动结果与状态快照 |
HttpServerEvent / HttpServerEventListener |
生命周期、错误事件与监听器签名 |
HttpServerFail / HttpServerErrorCode |
API 调用失败对象与稳定错误码 |
HttpHeaders / HttpJsonBody |
UTSJSONObject 别名,用于跨 UTS / 原生桥接的可序列化对象 |
路由回调会跨越 UTS 与原生层,参数和响应必须可序列化。JSON、query、params、headers 与 form 使用 UTSJSONObject;文件内容通过 filePath 返回,不直接在回调中传递二进制对象。
配置项(createHttpServer)
| 选项 | 默认 | 说明 |
|---|---|---|
host |
'loopback' |
'loopback' 仅本机回环;'lan' 开放到局域网 |
port |
0 |
0 由系统分配空闲端口;否则监听指定端口 |
logEnabled |
false |
是否输出生命周期与错误日志 |
maxConnections |
16 |
最大并发连接数 |
requestTimeout |
30000 |
单次请求处理超时(毫秒) |
maxRequestBodySize |
8 MiB |
请求体上限(字节),超出返回 413 |
uploadDir |
应用临时目录 | multipart 文件落盘目录 |
maxUploadFileCount |
20 |
单次上传文件数量上限 |
maxUploadFileSize |
16 MiB |
单个上传文件大小上限(字节) |
cors |
null(关闭) |
CORS 配置,见下表 |
multipart 请求的总大小仍受 maxRequestBodySize 限制。需要接收大文件时,应同时调整请求体上限与单文件上限。
cors 配置:
| 字段 | 说明 |
|---|---|
allowedOrigins |
必填;来源白名单,['*'] 表示允许任意;空数组等价于 ['*'] |
allowedMethods |
允许的方法;省略时为 GET / POST / PUT / PATCH / DELETE / OPTIONS,如需 HEAD 应显式加入 |
allowedHeaders |
允许的请求头,省略时回显客户端请求的头 |
allowCredentials |
是否允许携带凭证(为 true 时不回写 *,回显具体 Origin) |
maxAgeSeconds |
预检缓存时长 |
静态目录配置(StaticMountOptions)
mount() 只能在服务未运行时调用。同一路径下优先查找索引文件;找不到索引时,只有显式开启目录列举才会返回目录内容。
| 字段 | 默认 | 说明 |
|---|---|---|
root |
无 | 必填;平台可读取的目录路径,应用资源建议通过 getResourcePath() 获取 |
index |
'index.html' |
目录索引文件;可使用逗号分隔多个候选,例如 'index.html,index.htm' |
cacheControl |
不发送 | 静态响应的 Cache-Control 值 |
allowDirectoryListing |
false |
找不到索引文件时是否生成目录列表 |
服务实例方法
| 方法 | 说明 |
|---|---|
mount(path, options) |
挂载静态目录(仅服务未运行时) |
route(method, path, handler) |
注册动态路由(仅服务未运行时) |
respond(responseId, response) |
完成动态请求;responseId 由路由回调提供 |
start() |
启动监听,返回 Promise<HttpServerInfo>(含 urls) |
stop() |
停止监听,可再次 start |
destroy() |
永久释放实例 |
status() |
返回当前状态快照 |
on(event, listener) / off(event, id?) |
订阅 / 取消订阅事件(starting/started/stopping/stopped/error) |
服务状态按 created -> starting -> running -> stopping -> stopped 变化,destroy() 后进入 destroyed。stop() 后可以再次 start();destroy() 后不能再次启动。
请求 HttpRequest
| 字段 | 说明 |
|---|---|
id |
请求标识 |
method / path |
方法与路径 |
query |
查询字符串键值(已按段解析) |
params |
:param 路径参数 |
headers |
请求头(键名小写) |
remoteAddress |
对端 IP |
contentType / contentLength |
内容类型与长度 |
bodyText |
文本类请求体(非文本类为 null) |
bodyJson |
application/json 请求体(解析失败为 null) |
form |
multipart / urlencoded 普通字段 |
files |
上传文件列表(fieldName/fileName/mimeType/size/path) |
响应 HttpRouteResponse(一次性)
原生路由回调的参数必须是可序列化数据,因此处理器收到 responseId,再通过 server.respond(responseId, response) 完成请求。每个请求只能响应一次;超过 requestTimeout 仍未响应时返回 504。
| 字段 | 说明 |
|---|---|
statusCode |
HTTP 状态码(必填) |
headers |
可选响应头数组,元素为 { name, value } |
bodyJson |
JSON 正文;自动使用 application/json; charset=utf-8 |
bodyText |
文本正文;自动使用 text/plain; charset=utf-8 |
filePath |
文件路径;自动支持 Range / HEAD |
contentType |
可选内容类型,会覆盖正文类型的默认值 |
bodyJson、bodyText、filePath 最多设置一个;都不设置时发送空响应。
路由处理器可以在回调返回后再调用 respond(),但必须在 requestTimeout 内完成。超时会返回 504,之后再使用该 responseId 会按“响应已提交或不再等待”处理。
返回对象与事件
start() 返回 HttpServerInfo:
| 字段 | 说明 |
|---|---|
id |
服务实例 ID |
host |
实际绑定地址;loopback 通常为 127.0.0.1,LAN 通常为 0.0.0.0 |
port |
实际监听端口;配置 port: 0 时由系统分配 |
urls |
可直接访问的 URL 列表;LAN 模式会尽量包含实际 IPv4 地址 |
startedAt |
启动成功时的毫秒时间戳 |
status() 返回 HttpServerStatus:
| 字段 | 说明 |
|---|---|
id |
服务实例 ID |
state |
created / starting / running / stopping / stopped / destroyed |
isRunning |
当前是否已经开始监听 |
routeCount / mountCount |
已注册的动态路由数和静态挂载数 |
info |
运行时的 HttpServerInfo;未运行时为 null |
生命周期监听器收到 HttpServerEvent:
| 字段 | 说明 |
|---|---|
type |
事件名:starting / started / stopping / stopped / error |
serverId |
服务实例 ID |
timestamp |
事件发生时的毫秒时间戳 |
message |
生命周期或错误摘要 |
info |
可用时携带 HttpServerInfo,否则为 null |
data |
可选扩展数据,当前无数据时为 null |
默认值与安全
- 默认仅 loopback:服务默认只监听本机回环地址;开放到局域网必须显式设置
host: 'lan'。 - 静态路径限定:静态服务将相对路径中的
..等片段清洗并二次校验,确保响应文件不会逃逸出挂载根目录,越界请求返回403。 - 上传落盘受控:multipart 文件由插件写入受控
uploadDir(文件名随机化),客户端无法指定目标目录;超过数量 / 大小限制的请求会被拒绝。 - 上传文件清理:上传文件不会由插件自动删除,业务处理完成后必须由宿主清理。Android 与 iOS 不承诺完全一致的失败残留清理行为,不要依赖失败请求自动清理目录。
- 请求体限额:超过
maxRequestBodySize的请求返回413。 - CORS 默认关闭:未配置
cors时不回写任何跨域头。 - 日志安全:日志默认关闭;开启后仅记录生命周期与错误摘要,不写入 Cookie / Authorization / 请求体。
错误码
错误码跨平台稳定,errSubject 固定为 hans-http-server。
import { HttpServerFail } from '@/uni_modules/hans-http-server'
try {
const info = await server.start()
console.log('listening', info.urls)
} catch (error) {
const fail = error as HttpServerFail
console.error(fail.errCode, fail.errMsg, fail.detail)
}
HttpServerFail 包含 errCode、errSubject、errMsg 和可空的 detail。HTTP 请求解析或尺寸超限会直接向客户端返回对应 HTTP 状态;下表主要用于调用插件 API 时捕获失败。
| errCode | 含义 |
|---|---|
9015801 |
配置非法 |
9015802 |
生命周期状态非法(如 destroy 后再调用、运行中重复 start) |
9015803 |
端口被占用 |
9015804 |
启动失败 |
9015805 |
路由 / 挂载注册非法 |
9015806 |
响应已提交(重复终止响应) |
9015807 |
路由处理器失败 |
9015808 |
当前平台不支持该能力 |
9015809 |
静态路径不被允许 |
9015810 |
请求超过尺寸限制 |
9015899 |
未知错误 |
版本要求
| 范围 | 要求或验证基线 |
|---|---|
| uni-app x | HBuilderX 4.25+、uni-app x 3.1.0+ |
| 传统 uni-app 兼容 | uni-app 3.1.0+,仅 Vue2 / Vue3 的 Android 与 iOS App |
| Android | minSdkVersion 21 |
| iOS | deploymentTarget 12 |
功能限制
- HTTPS / TLS
- WebSocket / SSE / 流式响应
- HTTP pipelining
- HLS / mp4 的跨平台播放器兼容性保证
- 请求体 chunked transfer encoding
- App 进入后台后的持续运行(保活由宿主应用负责)
- IPv6 监听

收藏人数:
购买源码授权版(
试用
使用 HBuilderX 导入示例项目
赞赏(0)
下载 385
赞赏 0
下载 12456726
赞赏 1935
赞赏
京公网安备:11010802035340号