这是一个基于 HarmonyOS 的轻量级Web服务器框架,提供了类似 Express.js 的 API 设计,支持路由、中间件、静态文件服务等功能。
-
类 Express.js 的 API 设计
-
支持路由参数和查询字符串
-
内置多种请求体解析器
-
CORS 跨域支持
-
静态文件服务
-
文件上传支持
-
缓存控制
-
错误处理
-
中间件系统
-
多种日志格式支持
ohpm install @cxy/webserver或oh-package.json5 添加依赖,然后同步项目
{
"dependencies": {
"@cxy/webserver": "^1.0.0"
}
}import { WebServer } from '@cxy/webserver';
this.server = new WebServer();
// 注册 GET / 接口
this.server.get('/', (req, res, next) => {
res.status(200).json({
message: '欢迎使用 WebServer'
})
})
// 在8080端口 启动服务器
const info = await this.server.startServer(8080);
if (info.address) {
console.log(`http://${info.address}:${info.port}`)
} else {
console.error("启动失败,未获取到地址");
}
// 访问:http://设备的ip:8080/ 完整的代码示例请查看 demo
import { WebServer } from '@cxy/webserver';
// 初始化服务器
initServer() {
this.server = new WebServer();
// --- 1. 中间件注册 ---
// 顺序很重要,通常日志和CORS最先,然后是请求体解析,再是静态文件和路由
this.server.logger({
stream: (log: string) => {
console.log(log) //自定义写入日志文件
}
}) //日志记录
this.server.cors(); //支持跨域
this.server.auto(); //自动解析
// this.server.json(); // 解析 application/json
// this.server.urlencoded(); // 解析 application/x-www-form-urlencoded
// this.server.multipart(); // 解析 multipart/form-data (用于文件上传)
// this.server.plain(); // 解析文本
this.server.serveStatic(this.staticFilesRoot); // 提供静态文件服务
// --- 2. 模拟数据 ---
const users: User[] = [
{ id: 1, name: 'cxy' },
{ id: 2, name: 'ihongren' },
{ id: 3, name: '仙银' }
];
let nextUserId = 4;
// --- 3. API 示例 ---
// GET /api/users - 获取所有用户
// curl http://192.168.2.38:8080/api/users
this.server.get('/api/users', (req, res) => {
res.json(users);
});
// GET /api/users/:id - 使用路由参数,获取单个用户
// curl http://192.168.2.38:8080/api/users/1
this.server.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (user) {
res.json(user);
} else {
res.status(404).json({ error: 'User not found' });
}
});
// POST /api/users - 创建新用户
// curl -X POST -H "Content-Type: application/json" -d '{"name":"NewUser"}' http://192.168.2.38:8080/api/users
this.server.post('/api/users', (req, res) => {
const newUser: User = {
id: nextUserId++,
name: (req.body as Record<string, string>).name || 'Unnamed'
};
users.push(newUser);
console.log('Created new user:', JSON.stringify(newUser));
res.status(201).json(newUser);
});
// post /api/users/:id - 更新用户
// curl -X POST -H "Content-Type: application/json" -d '{"name":"UpdatedUser"}' http://192.168.2.38:8080/api/users/1
this.server.post('/api/users/:id', (req, res) => {
const userId = parseInt(req.params.id);
const userIndex = users.findIndex(u => u.id === userId);
if (userIndex !== -1) {
users[userIndex].name = (req.body as Record<string, string>).name || users[userIndex].name;
res.json(users[userIndex]);
} else {
res.status(404).json({ error: 'User not found' });
}
});
// --- 4. 文件上传路由 ---
// curl -X POST -F "uploadFile=@/path/to/your/file.txt" http://192.168.2.38:8080/api/upload
this.server.post('/api/upload', async (req, res, next) => {
try {
const uploadedFile = req.files?.uploadFile; // 'uploadFile' 对应 HTML form 中的 input name
if (!uploadedFile) {
return res.status(400).json({ error: 'No file uploaded.' });
}
const context = this.getUIContext().getHostContext() as common.UIAbilityContext;
const tempPath = `${context.filesDir}/${uploadedFile.fileName}`;
const f = await fileIo.open(tempPath, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE)
await fileIo.write(f.fd, uploadedFile.data);
console.log(`File uploaded successfully: ${uploadedFile.fileName}`);
res.json({
message: 'File uploaded successfully!',
filename: uploadedFile.fileName,
size: uploadedFile.data.byteLength,
contentType: uploadedFile.contentType,
savedTo: tempPath
});
} catch (error) {
next(error);
}
});
// --- 5. 其他高级示例 ---
// 路由参数示例
// curl http://192.168.2.38:8080/api/users/123/books/456
this.server.get('/api/users/:userId/books/:bookId', (req, res) => {
res.json({
message: `You requested book ${req.params.bookId} for user ${req.params.userId}.`
});
});
// 获取自定义请求头示例
// curl -H "X-Custom-Request-Header: MyValue" http://192.168.2.38:8080/api/custom-request-header
this.server.get('/api/custom-request-header', (req, res) => {
const customHeader = req.get('x-custom-request-header');
res.json({
message: 'Received custom request header',
headerValue: customHeader || 'Not found'
});
});
// 自定义响应头示例
// curl -i http://192.168.2.38:8080/api/custom-header
this.server.get('/api/custom-header', (req, res) => {
res.setHeader('X-Custom-Header', 'Hello from WebServer!');
res.json({ message: 'Check the response headers!' });
});
// 错误触发示例
// curl http://192.168.2.38:8080/crash
this.server.get('/crash', (req, res, next) => {
// 故意抛出一个错误来测试错误处理中间件
throw new Error('This is a simulated crash!');
});
// --- 6. 统一错误处理中间件 (必须在路由之后注册) ---
const customErrorHandler: ErrorHandler = (error, req, res, next) => {
console.error(`[WebServer Error] Path: ${req.path}, Message: ${error.message}`);
if (res.isHeadersSent()) {
return next(error); // 如果头已发送,则委托给默认错误处理器
}
res.status(500).json({
error: 'Internal Server Error',
message: error.message || 'An unknown error occurred.'
});
};
this.server.use(customErrorHandler);
}
//启动服务器
const info = await this.server.startServer(8080);
if (info.address) {
console.log(`http://${info.address}:${info.port}`)
} else {
console.error("启动失败,未获取到地址");
}
// 停止服务器
await this.server.stopServer();.
├── body-parser
│ ├── BodyParserExample.ets
│ └── BodyParser中间件说明.md
├── cors
│ ├── CorsExample.ets
│ └── 跨域中间件说明.md
├── logger
│ ├── LoggerExample.ets
│ └── 日志中间件说明.md
├── router
│ ├── RouterExample.ets
│ └── 路由说明.md
└── static
├── StaticExample.ets
└── 静态文件服务中间件说明.md运行 demo
| 未开启服务 | 已开启服务 |
|---|---|
![]() |
![]() |
浏览器访问:http://192.168.xx.xx:8080
WebServer API 文档
Web服务器主类,提供HTTP服务器功能。
setConfig(key: string, value: Object)- 设置配置项getConfig(key: string): Object | undefined- 获取配置项use(handler: RequestHandler | ErrorHandler)- 注册中间件get(path: string, handler: RequestHandler)- 注册GET路由post(path: string, handler: RequestHandler)- 注册POST路由put(path: string, handler: RequestHandler)- 注册PUT路由delete(path: string, handler: RequestHandler)- 注册DELETE路由json()- 启用JSON请求体解析urlencoded()- 启用URL编码请求体解析multipart()- 启用多部分表单解析plain()- 启用文本请求体解析serveStatic(directoryPath: string, options?: CacheOptions)- 启用静态文件服务cors(options?: CorsOptions)- 启用CORS跨域支持logger(options?: LoggerOptions)- 启用日志中间件startServer(port: number): Promise<ServerInfo>- 启动服务器stopServer(): Promise<void>- 停止服务器
HTTP请求解析类,包含请求的所有信息。
method: string- HTTP请求方法path: string- 请求路径url: string- 完整URL路径ip: string- 客户端IP地址headers: Map<string, string>- 请求头集合body: any- 解析后的请求体数据query: Map<string, string>- 查询字符串参数params: Record<string, string>- 路由参数files: Record<string, File>- 上传的文件
parseBody(): void- 解析请求体数据getRawBody(): ArrayBuffer- 获取原始请求体数据get(headerName: string): string | undefined- 获取请求头is(type: string): boolean- 检查Content-Typeget userAgent(): string- 获取User-Agentget referer(): string- 获取Refererget contentLength(): number- 获取Content-Length
HTTP响应构建类,用于构建和发送响应。
isHeadersSent(): boolean- 检查响应头是否已发送setHeader(name: string, value: string): HttpResponse- 设置响应头status(code: number): HttpResponse- 设置HTTP状态码getStatusCode(): number- 获取HTTP状态码send(body?: string | ArrayBuffer): Promise<void>- 发送响应数据json(data: ESObject): Promise<void>- 发送JSON响应onFinish(callback: ResponseFinishCallback): void- 添加响应完成回调
路由管理器,负责路由的注册、匹配和执行。
addRoute(method: string, path: string, handler: RequestHandler | ErrorHandler)- 添加路由handle(req: HttpRequest, res: HttpResponse)- 处理HTTP请求getRoutes(): Route[]- 获取所有路由
请求体解析中间件。
static json(): RequestHandler- JSON解析中间件static urlencoded(): RequestHandler- URL编码解析中间件static plain(): RequestHandler- 普通文本解析中间件static multipart(): RequestHandler- 多部分表单解析中间件static auto(): RequestHandler- 通用解析中间件
CORS跨域资源共享中间件。
static create(options?: CorsOptions): RequestHandler- 创建CORS中间件
静态文件服务中间件。
static serve(directoryPath: string, options?: CacheOptions): RequestHandler- 创建静态文件服务中间件
日志中间件,提供HTTP请求日志记录功能。
static create(options?: LoggerOptions): RequestHandler- 创建自定义日志中间件static dev(): RequestHandler- 开发环境日志格式static combined(): RequestHandler- 生产环境日志格式(Apache Combined)static common(): RequestHandler- 通用日志格式(Apache Common)static short(): RequestHandler- 简短日志格式static tiny(): RequestHandler- 最简日志格式
通用工具类,提供各种实用方法。
static arrayBufferToStr(arr: ArrayBuffer): string- ArrayBuffer转字符串static strToArrayBuffer(str: string): ArrayBuffer- 字符串转ArrayBufferstatic mergeArrayBuffers(buffer1: ArrayBuffer, buffer2: ArrayBuffer): ArrayBuffer- 合并ArrayBufferstatic getMimeType(filePath: string): string- 获取MIME类型static normalizePath(path: string): string- 规范化路径static joinPath(...paths: string[]): string- 拼接路径static sanitizeFilename(filename: string): string- 清理文件名
NextFunction- 下一步函数类型RequestHandler- 请求处理函数类型ErrorHandler- 错误处理函数类型
File- 上传文件接口Route- 路由接口CorsOptions- CORS配置选项CacheOptions- 缓存配置选项LoggerOptions- 日志配置选项ServerInfo- 服务器信息接口
LogFormat- 日志格式枚举(DEV, TINY, SHORT, COMMON, COMBINED)
❓如果是使用过程中有什么问题,欢迎提 issues
@仙银 鸿蒙相关开源作品
1、hpack - 鸿蒙内部测试分发,一键脚本打包工具
2、Open-in-DevEco-Studio - macOS 直接在 Finder 工具栏上,使用 DevEco-Studio 打开鸿蒙工程。
3、cxy-theme - DevEco-Studio 绿色背景主题
4、harmony-udid-tool - 简单易用的 HarmonyOS 设备 UDID 获取工具,适用于非开发人员。
5、SandboxFinder - 鸿蒙沙箱文件浏览器
6、WebServer - 鸿蒙轻量级Web服务器框架
7、SelectableMenu - 适用于聊天对话框中的文本选择菜单
🌟 如果项目对你有帮助,欢迎持续关注和 Star ,赞助


