docs(network): add HTTP routing documentation (#415)

Add comprehensive HTTP routing documentation for the server module:
- Create new http.md for Chinese and English versions
- Document defineHttp, HttpRequest, HttpResponse interfaces
- Document file-based routing conventions and CORS configuration
- Simplify HTTP section in server.md with link to detailed docs
This commit is contained in:
YHH
2025-12-31 22:53:38 +08:00
committed by GitHub
parent 15c1d98305
commit ff549f3c2a
5 changed files with 1006 additions and 225 deletions

View File

@@ -267,6 +267,7 @@ export default defineConfig({
{ label: '概述', slug: 'modules/network', translations: { en: 'Overview' } },
{ label: '客户端', slug: 'modules/network/client', translations: { en: 'Client' } },
{ label: '服务器', slug: 'modules/network/server', translations: { en: 'Server' } },
{ label: 'HTTP 路由', slug: 'modules/network/http', translations: { en: 'HTTP Routing' } },
{ label: '认证系统', slug: 'modules/network/auth', translations: { en: 'Authentication' } },
{ label: '速率限制', slug: 'modules/network/rate-limit', translations: { en: 'Rate Limiting' } },
{ label: '状态同步', slug: 'modules/network/sync', translations: { en: 'State Sync' } },

View File

@@ -0,0 +1,490 @@
---
title: "HTTP Routing"
description: "HTTP REST API routing with WebSocket port sharing support"
---
`@esengine/server` includes a lightweight HTTP routing feature that can share the same port with WebSocket services, making it easy to implement REST APIs.
## Quick Start
### Inline Route Definition
The simplest way is to define HTTP routes directly when creating the server:
```typescript
import { createServer } from '@esengine/server'
const server = await createServer({
port: 3000,
http: {
'/api/health': (req, res) => {
res.json({ status: 'ok', time: Date.now() })
},
'/api/users': {
GET: (req, res) => {
res.json({ users: [] })
},
POST: async (req, res) => {
const body = req.body as { name: string }
res.status(201).json({ id: '1', name: body.name })
}
}
},
cors: true // Enable CORS
})
await server.start()
```
### File-based Routing
For larger projects, file-based routing is recommended. Create a `src/http` directory where each file corresponds to a route:
```typescript
// src/http/login.ts
import { defineHttp } from '@esengine/server'
interface LoginBody {
username: string
password: string
}
export default defineHttp<LoginBody>({
method: 'POST',
handler(req, res) {
const { username, password } = req.body as LoginBody
// Validate user...
if (username === 'admin' && password === '123456') {
res.json({ token: 'jwt-token-here', userId: 'user-1' })
} else {
res.error(401, 'Invalid username or password')
}
}
})
```
```typescript
// server.ts
import { createServer } from '@esengine/server'
const server = await createServer({
port: 3000,
httpDir: './src/http', // HTTP routes directory
httpPrefix: '/api', // Route prefix
cors: true
})
await server.start()
// Route: POST /api/login
```
## defineHttp Definition
`defineHttp` is used to define type-safe HTTP handlers:
```typescript
import { defineHttp } from '@esengine/server'
interface CreateUserBody {
username: string
email: string
password: string
}
export default defineHttp<CreateUserBody>({
// HTTP method (default POST)
method: 'POST',
// Handler function
handler(req, res) {
const body = req.body as CreateUserBody
// Handle request...
res.status(201).json({ id: 'new-user-id' })
}
})
```
### Supported HTTP Methods
```typescript
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS'
```
## HttpRequest Object
The HTTP request object contains the following properties:
```typescript
interface HttpRequest {
/** Raw Node.js IncomingMessage */
raw: IncomingMessage
/** HTTP method */
method: string
/** Request path */
path: string
/** Query parameters */
query: Record<string, string>
/** Request headers */
headers: Record<string, string | string[] | undefined>
/** Parsed request body */
body: unknown
/** Client IP */
ip: string
}
```
### Usage Examples
```typescript
export default defineHttp({
method: 'GET',
handler(req, res) {
// Get query parameters
const page = parseInt(req.query.page ?? '1')
const limit = parseInt(req.query.limit ?? '10')
// Get request headers
const authHeader = req.headers.authorization
// Get client IP
console.log('Request from:', req.ip)
res.json({ page, limit })
}
})
```
### Body Parsing
The request body is automatically parsed based on `Content-Type`:
- `application/json` - Parsed as JSON object
- `application/x-www-form-urlencoded` - Parsed as key-value object
- Others - Kept as raw string
```typescript
export default defineHttp<{ name: string; age: number }>({
method: 'POST',
handler(req, res) {
// body is already parsed
const { name, age } = req.body as { name: string; age: number }
res.json({ received: { name, age } })
}
})
```
## HttpResponse Object
The HTTP response object provides a chainable API:
```typescript
interface HttpResponse {
/** Raw Node.js ServerResponse */
raw: ServerResponse
/** Set status code */
status(code: number): HttpResponse
/** Set response header */
header(name: string, value: string): HttpResponse
/** Send JSON response */
json(data: unknown): void
/** Send text response */
text(data: string): void
/** Send error response */
error(code: number, message: string): void
}
```
### Usage Examples
```typescript
export default defineHttp({
method: 'POST',
handler(req, res) {
// Set status code and custom headers
res
.status(201)
.header('X-Custom-Header', 'value')
.json({ created: true })
}
})
```
```typescript
export default defineHttp({
method: 'GET',
handler(req, res) {
// Send error response
res.error(404, 'Resource not found')
// Equivalent to: res.status(404).json({ error: 'Resource not found' })
}
})
```
```typescript
export default defineHttp({
method: 'GET',
handler(req, res) {
// Send plain text
res.text('Hello, World!')
}
})
```
## File Routing Conventions
### Name Conversion
File names are automatically converted to route paths:
| File Path | Route Path (prefix=/api) |
|-----------|-------------------------|
| `login.ts` | `/api/login` |
| `users/profile.ts` | `/api/users/profile` |
| `users/[id].ts` | `/api/users/:id` |
| `game/room/[roomId].ts` | `/api/game/room/:roomId` |
### Dynamic Route Parameters
Use `[param]` syntax to define dynamic parameters:
```typescript
// src/http/users/[id].ts
import { defineHttp } from '@esengine/server'
export default defineHttp({
method: 'GET',
handler(req, res) {
// Get parameter from path
// Note: current version requires manual path parsing
const id = req.path.split('/').pop()
res.json({ userId: id })
}
})
```
### Skip Rules
The following files are automatically skipped:
- Files starting with `_` (e.g., `_helper.ts`)
- `index.ts` / `index.js` files
- Non `.ts` / `.js` / `.mts` / `.mjs` files
### Directory Structure Example
```
src/
└── http/
├── _utils.ts # Skipped (underscore prefix)
├── index.ts # Skipped (index file)
├── health.ts # GET /api/health
├── login.ts # POST /api/login
├── register.ts # POST /api/register
└── users/
├── index.ts # Skipped
├── list.ts # GET /api/users/list
└── [id].ts # GET /api/users/:id
```
## CORS Configuration
### Quick Enable
```typescript
const server = await createServer({
port: 3000,
cors: true // Use default configuration
})
```
### Custom Configuration
```typescript
const server = await createServer({
port: 3000,
cors: {
// Allowed origins
origin: ['http://localhost:5173', 'https://myapp.com'],
// Or use wildcard
// origin: '*',
// origin: true, // Reflect request origin
// Allowed HTTP methods
methods: ['GET', 'POST', 'PUT', 'DELETE'],
// Allowed request headers
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
// Allow credentials (cookies)
credentials: true,
// Preflight cache max age (seconds)
maxAge: 86400
}
})
```
### CorsOptions Type
```typescript
interface CorsOptions {
/** Allowed origins: string, string array, true (reflect) or '*' */
origin?: string | string[] | boolean
/** Allowed HTTP methods */
methods?: string[]
/** Allowed request headers */
allowedHeaders?: string[]
/** Allow credentials */
credentials?: boolean
/** Preflight cache max age (seconds) */
maxAge?: number
}
```
## Route Merging
File routes and inline routes can be used together, with inline routes having higher priority:
```typescript
const server = await createServer({
port: 3000,
httpDir: './src/http',
httpPrefix: '/api',
// Inline routes merge with file routes
http: {
'/health': (req, res) => res.json({ status: 'ok' }),
'/api/special': (req, res) => res.json({ special: true })
}
})
```
## Sharing Port with WebSocket
HTTP routes automatically share the same port with WebSocket services:
```typescript
const server = await createServer({
port: 3000,
// WebSocket related config
apiDir: './src/api',
msgDir: './src/msg',
// HTTP related config
httpDir: './src/http',
httpPrefix: '/api',
cors: true
})
await server.start()
// Same port 3000:
// - WebSocket: ws://localhost:3000
// - HTTP API: http://localhost:3000/api/*
```
## Complete Examples
### Game Server Login API
```typescript
// src/http/auth/login.ts
import { defineHttp } from '@esengine/server'
import { createJwtAuthProvider } from '@esengine/server/auth'
interface LoginRequest {
username: string
password: string
}
interface LoginResponse {
token: string
userId: string
expiresAt: number
}
const jwtProvider = createJwtAuthProvider({
secret: process.env.JWT_SECRET!,
expiresIn: 3600
})
export default defineHttp<LoginRequest>({
method: 'POST',
async handler(req, res) {
const { username, password } = req.body as LoginRequest
// Validate user
const user = await db.users.findByUsername(username)
if (!user || !await verifyPassword(password, user.passwordHash)) {
res.error(401, 'Invalid username or password')
return
}
// Generate JWT
const token = jwtProvider.sign({
sub: user.id,
name: user.username,
roles: user.roles
})
const response: LoginResponse = {
token,
userId: user.id,
expiresAt: Date.now() + 3600 * 1000
}
res.json(response)
}
})
```
### Game Data Query API
```typescript
// src/http/game/leaderboard.ts
import { defineHttp } from '@esengine/server'
export default defineHttp({
method: 'GET',
async handler(req, res) {
const limit = parseInt(req.query.limit ?? '10')
const offset = parseInt(req.query.offset ?? '0')
const players = await db.players.findMany({
sort: { score: 'desc' },
limit,
offset
})
res.json({
data: players,
pagination: { limit, offset }
})
}
})
```
## Best Practices
1. **Use defineHttp** - Get better type hints and code organization
2. **Unified Error Handling** - Use `res.error()` to return consistent error format
3. **Enable CORS** - Required for frontend-backend separation
4. **Directory Organization** - Organize HTTP route files by functional modules
5. **Validate Input** - Always validate `req.body` and `req.query` content
6. **Status Code Standards** - Follow HTTP status code conventions (200, 201, 400, 401, 404, 500, etc.)

View File

@@ -90,128 +90,21 @@ await server.start()
Supports HTTP API sharing the same port with WebSocket, ideal for login, registration, and similar scenarios.
### File-based Routing
Create route files in the `httpDir` directory, automatically mapped to HTTP endpoints:
```
src/http/
├── login.ts → POST /api/login
├── register.ts → POST /api/register
├── health.ts → GET /api/health (set method: 'GET')
└── users/
└── [id].ts → POST /api/users/:id (dynamic route)
```
### Define Routes
Use `defineHttp` to define type-safe route handlers:
```typescript
// src/http/login.ts
import { defineHttp } from '@esengine/server'
const server = await createServer({
port: 3000,
httpDir: './src/http', // HTTP routes directory
httpPrefix: '/api', // Route prefix
cors: true,
interface LoginBody {
username: string
password: string
}
export default defineHttp<LoginBody>({
method: 'POST', // Default POST, options: GET/PUT/DELETE/PATCH
handler(req, res) {
const { username, password } = req.body
// Validate credentials...
if (!isValid(username, password)) {
res.error(401, 'Invalid credentials')
return
}
// Generate token...
res.json({ token: '...', userId: '...' })
// Or inline definition
http: {
'/health': (req, res) => res.json({ status: 'ok' })
}
})
```
### Request Object (HttpRequest)
```typescript
interface HttpRequest {
raw: IncomingMessage // Node.js raw request
method: string // Request method
path: string // Request path
query: Record<string, string> // Query parameters
headers: Record<string, string | string[] | undefined>
body: unknown // Parsed JSON body
ip: string // Client IP
}
```
### Response Object (HttpResponse)
```typescript
interface HttpResponse {
raw: ServerResponse // Node.js raw response
status(code: number): HttpResponse // Set status code (chainable)
header(name: string, value: string): HttpResponse // Set header (chainable)
json(data: unknown): void // Send JSON
text(data: string): void // Send text
error(code: number, message: string): void // Send error
}
```
### Usage Example
```typescript
// Complete login server example
import { createServer, defineHttp } from '@esengine/server'
import { createJwtAuthProvider, withAuth } from '@esengine/server/auth'
const jwtProvider = createJwtAuthProvider({
secret: process.env.JWT_SECRET!,
expiresIn: 3600 * 24,
})
const server = await createServer({
port: 8080,
httpDir: 'src/http',
httpPrefix: '/api',
cors: true,
})
// Wrap with auth (WebSocket connections validate token)
const authServer = withAuth(server, {
provider: jwtProvider,
extractCredentials: (req) => {
const url = new URL(req.url, 'http://localhost')
return url.searchParams.get('token')
},
})
await authServer.start()
// HTTP: http://localhost:8080/api/*
// WebSocket: ws://localhost:8080?token=xxx
```
### Inline Routes
Routes can also be defined directly in configuration (merged with file routes, inline takes priority):
```typescript
const server = await createServer({
port: 8080,
http: {
'/health': {
GET: (req, res) => res.json({ status: 'ok' }),
},
'/webhook': async (req, res) => {
// Accepts all methods
await handleWebhook(req.body)
res.json({ received: true })
},
},
})
```
> For detailed documentation, see [HTTP Routing](/en/modules/network/http)
## Room System

View File

@@ -0,0 +1,490 @@
---
title: "HTTP 路由"
description: "HTTP REST API 路由功能,支持与 WebSocket 共用端口"
---
`@esengine/server` 内置了轻量级的 HTTP 路由功能,可以与 WebSocket 服务共用同一端口,方便实现 REST API。
## 快速开始
### 内联路由定义
最简单的方式是在创建服务器时直接定义 HTTP 路由:
```typescript
import { createServer } from '@esengine/server'
const server = await createServer({
port: 3000,
http: {
'/api/health': (req, res) => {
res.json({ status: 'ok', time: Date.now() })
},
'/api/users': {
GET: (req, res) => {
res.json({ users: [] })
},
POST: async (req, res) => {
const body = req.body as { name: string }
res.status(201).json({ id: '1', name: body.name })
}
}
},
cors: true // 启用 CORS
})
await server.start()
```
### 文件路由
对于较大的项目,推荐使用文件路由。创建 `src/http` 目录,每个文件对应一个路由:
```typescript
// src/http/login.ts
import { defineHttp } from '@esengine/server'
interface LoginBody {
username: string
password: string
}
export default defineHttp<LoginBody>({
method: 'POST',
handler(req, res) {
const { username, password } = req.body as LoginBody
// 验证用户...
if (username === 'admin' && password === '123456') {
res.json({ token: 'jwt-token-here', userId: 'user-1' })
} else {
res.error(401, '用户名或密码错误')
}
}
})
```
```typescript
// server.ts
import { createServer } from '@esengine/server'
const server = await createServer({
port: 3000,
httpDir: './src/http', // HTTP 路由目录
httpPrefix: '/api', // 路由前缀
cors: true
})
await server.start()
// 路由: POST /api/login
```
## defineHttp 定义
`defineHttp` 用于定义类型安全的 HTTP 处理器:
```typescript
import { defineHttp } from '@esengine/server'
interface CreateUserBody {
username: string
email: string
password: string
}
export default defineHttp<CreateUserBody>({
// HTTP 方法(默认 POST
method: 'POST',
// 处理函数
handler(req, res) {
const body = req.body as CreateUserBody
// 处理请求...
res.status(201).json({ id: 'new-user-id' })
}
})
```
### 支持的 HTTP 方法
```typescript
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS'
```
## HttpRequest 对象
HTTP 请求对象包含以下属性:
```typescript
interface HttpRequest {
/** 原始 Node.js IncomingMessage */
raw: IncomingMessage
/** HTTP 方法 */
method: string
/** 请求路径 */
path: string
/** 查询参数 */
query: Record<string, string>
/** 请求头 */
headers: Record<string, string | string[] | undefined>
/** 解析后的请求体 */
body: unknown
/** 客户端 IP */
ip: string
}
```
### 使用示例
```typescript
export default defineHttp({
method: 'GET',
handler(req, res) {
// 获取查询参数
const page = parseInt(req.query.page ?? '1')
const limit = parseInt(req.query.limit ?? '10')
// 获取请求头
const authHeader = req.headers.authorization
// 获取客户端 IP
console.log('Request from:', req.ip)
res.json({ page, limit })
}
})
```
### 请求体解析
请求体会根据 `Content-Type` 自动解析:
- `application/json` - 解析为 JSON 对象
- `application/x-www-form-urlencoded` - 解析为键值对对象
- 其他 - 保持原始字符串
```typescript
export default defineHttp<{ name: string; age: number }>({
method: 'POST',
handler(req, res) {
// body 已自动解析
const { name, age } = req.body as { name: string; age: number }
res.json({ received: { name, age } })
}
})
```
## HttpResponse 对象
HTTP 响应对象提供链式 API
```typescript
interface HttpResponse {
/** 原始 Node.js ServerResponse */
raw: ServerResponse
/** 设置状态码 */
status(code: number): HttpResponse
/** 设置响应头 */
header(name: string, value: string): HttpResponse
/** 发送 JSON 响应 */
json(data: unknown): void
/** 发送文本响应 */
text(data: string): void
/** 发送错误响应 */
error(code: number, message: string): void
}
```
### 使用示例
```typescript
export default defineHttp({
method: 'POST',
handler(req, res) {
// 设置状态码和自定义头
res
.status(201)
.header('X-Custom-Header', 'value')
.json({ created: true })
}
})
```
```typescript
export default defineHttp({
method: 'GET',
handler(req, res) {
// 发送错误响应
res.error(404, '资源不存在')
// 等价于: res.status(404).json({ error: '资源不存在' })
}
})
```
```typescript
export default defineHttp({
method: 'GET',
handler(req, res) {
// 发送纯文本
res.text('Hello, World!')
}
})
```
## 文件路由规范
### 命名转换
文件名会自动转换为路由路径:
| 文件路径 | 路由路径prefix=/api |
|---------|----------------------|
| `login.ts` | `/api/login` |
| `users/profile.ts` | `/api/users/profile` |
| `users/[id].ts` | `/api/users/:id` |
| `game/room/[roomId].ts` | `/api/game/room/:roomId` |
### 动态路由参数
使用 `[param]` 语法定义动态参数:
```typescript
// src/http/users/[id].ts
import { defineHttp } from '@esengine/server'
export default defineHttp({
method: 'GET',
handler(req, res) {
// 从路径获取参数
// 注意:当前版本需要手动解析 path
const id = req.path.split('/').pop()
res.json({ userId: id })
}
})
```
### 跳过规则
以下文件会被自动跳过:
-`_` 开头的文件(如 `_helper.ts`
- `index.ts` / `index.js` 文件
-`.ts` / `.js` / `.mts` / `.mjs` 文件
### 目录结构示例
```
src/
└── http/
├── _utils.ts # 跳过(下划线开头)
├── index.ts # 跳过index 文件)
├── health.ts # GET /api/health
├── login.ts # POST /api/login
├── register.ts # POST /api/register
└── users/
├── index.ts # 跳过
├── list.ts # GET /api/users/list
└── [id].ts # GET /api/users/:id
```
## CORS 配置
### 快速启用
```typescript
const server = await createServer({
port: 3000,
cors: true // 使用默认配置
})
```
### 自定义配置
```typescript
const server = await createServer({
port: 3000,
cors: {
// 允许的来源
origin: ['http://localhost:5173', 'https://myapp.com'],
// 或使用通配符
// origin: '*',
// origin: true, // 反射请求来源
// 允许的 HTTP 方法
methods: ['GET', 'POST', 'PUT', 'DELETE'],
// 允许的请求头
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'],
// 是否允许携带凭证cookies
credentials: true,
// 预检请求缓存时间(秒)
maxAge: 86400
}
})
```
### CorsOptions 类型
```typescript
interface CorsOptions {
/** 允许的来源字符串、字符串数组、true反射或 '*' */
origin?: string | string[] | boolean
/** 允许的 HTTP 方法 */
methods?: string[]
/** 允许的请求头 */
allowedHeaders?: string[]
/** 是否允许携带凭证 */
credentials?: boolean
/** 预检请求缓存时间(秒) */
maxAge?: number
}
```
## 路由合并
文件路由和内联路由可以同时使用,内联路由优先级更高:
```typescript
const server = await createServer({
port: 3000,
httpDir: './src/http',
httpPrefix: '/api',
// 内联路由会与文件路由合并
http: {
'/health': (req, res) => res.json({ status: 'ok' }),
'/api/special': (req, res) => res.json({ special: true })
}
})
```
## 与 WebSocket 共用端口
HTTP 路由与 WebSocket 服务自动共用同一端口:
```typescript
const server = await createServer({
port: 3000,
// WebSocket 相关配置
apiDir: './src/api',
msgDir: './src/msg',
// HTTP 相关配置
httpDir: './src/http',
httpPrefix: '/api',
cors: true
})
await server.start()
// 同一端口 3000
// - WebSocket: ws://localhost:3000
// - HTTP API: http://localhost:3000/api/*
```
## 完整示例
### 游戏服务器登录 API
```typescript
// src/http/auth/login.ts
import { defineHttp } from '@esengine/server'
import { createJwtAuthProvider } from '@esengine/server/auth'
interface LoginRequest {
username: string
password: string
}
interface LoginResponse {
token: string
userId: string
expiresAt: number
}
const jwtProvider = createJwtAuthProvider({
secret: process.env.JWT_SECRET!,
expiresIn: 3600
})
export default defineHttp<LoginRequest>({
method: 'POST',
async handler(req, res) {
const { username, password } = req.body as LoginRequest
// 验证用户
const user = await db.users.findByUsername(username)
if (!user || !await verifyPassword(password, user.passwordHash)) {
res.error(401, '用户名或密码错误')
return
}
// 生成 JWT
const token = jwtProvider.sign({
sub: user.id,
name: user.username,
roles: user.roles
})
const response: LoginResponse = {
token,
userId: user.id,
expiresAt: Date.now() + 3600 * 1000
}
res.json(response)
}
})
```
### 游戏数据查询 API
```typescript
// src/http/game/leaderboard.ts
import { defineHttp } from '@esengine/server'
export default defineHttp({
method: 'GET',
async handler(req, res) {
const limit = parseInt(req.query.limit ?? '10')
const offset = parseInt(req.query.offset ?? '0')
const players = await db.players.findMany({
sort: { score: 'desc' },
limit,
offset
})
res.json({
data: players,
pagination: { limit, offset }
})
}
})
```
## 最佳实践
1. **使用 defineHttp** - 获得更好的类型提示和代码组织
2. **统一错误处理** - 使用 `res.error()` 返回一致的错误格式
3. **启用 CORS** - 前后端分离时必须配置
4. **目录组织** - 按功能模块组织 HTTP 路由文件
5. **验证输入** - 始终验证 `req.body``req.query` 的内容
6. **状态码规范** - 遵循 HTTP 状态码规范200、201、400、401、404、500 等)

View File

@@ -90,128 +90,35 @@ await server.start()
支持 HTTP API 与 WebSocket 共用端口,适用于登录、注册等场景。
### 文件路由
`httpDir` 目录下创建路由文件,自动映射为 HTTP 端点:
```typescript
const server = await createServer({
port: 3000,
httpDir: './src/http', // HTTP 路由目录
httpPrefix: '/api', // 路由前缀
cors: true,
// 或内联定义
http: {
'/health': (req, res) => res.json({ status: 'ok' })
}
})
```
src/http/
├── login.ts → POST /api/login
├── register.ts → POST /api/register
├── health.ts → GET /api/health (需设置 method: 'GET')
└── users/
└── [id].ts → POST /api/users/:id (动态路由)
```
### 定义路由
使用 `defineHttp` 定义类型安全的路由处理器:
```typescript
// src/http/login.ts
import { defineHttp } from '@esengine/server'
interface LoginBody {
username: string
password: string
}
export default defineHttp<LoginBody>({
method: 'POST', // 默认 POST可选 GET/PUT/DELETE/PATCH
export default defineHttp<{ username: string; password: string }>({
method: 'POST',
handler(req, res) {
const { username, password } = req.body
// 验证凭证...
if (!isValid(username, password)) {
res.error(401, 'Invalid credentials')
return
}
// 生成 token...
res.json({ token: '...', userId: '...' })
// 验证并返回 token...
res.json({ token: '...' })
}
})
```
### 请求对象 (HttpRequest)
```typescript
interface HttpRequest {
raw: IncomingMessage // Node.js 原始请求
method: string // 请求方法
path: string // 请求路径
query: Record<string, string> // 查询参数
headers: Record<string, string | string[] | undefined>
body: unknown // 解析后的 JSON 请求体
ip: string // 客户端 IP
}
```
### 响应对象 (HttpResponse)
```typescript
interface HttpResponse {
raw: ServerResponse // Node.js 原始响应
status(code: number): HttpResponse // 设置状态码(链式)
header(name: string, value: string): HttpResponse // 设置头(链式)
json(data: unknown): void // 发送 JSON
text(data: string): void // 发送文本
error(code: number, message: string): void // 发送错误
}
```
### 使用示例
```typescript
// 完整的登录服务器示例
import { createServer, defineHttp } from '@esengine/server'
import { createJwtAuthProvider, withAuth } from '@esengine/server/auth'
const jwtProvider = createJwtAuthProvider({
secret: process.env.JWT_SECRET!,
expiresIn: 3600 * 24,
})
const server = await createServer({
port: 8080,
httpDir: 'src/http',
httpPrefix: '/api',
cors: true,
})
// 包装认证WebSocket 连接验证 token
const authServer = withAuth(server, {
provider: jwtProvider,
extractCredentials: (req) => {
const url = new URL(req.url, 'http://localhost')
return url.searchParams.get('token')
},
})
await authServer.start()
// HTTP: http://localhost:8080/api/*
// WebSocket: ws://localhost:8080?token=xxx
```
### 内联路由
也可以直接在配置中定义路由(与文件路由合并,内联优先):
```typescript
const server = await createServer({
port: 8080,
http: {
'/health': {
GET: (req, res) => res.json({ status: 'ok' }),
},
'/webhook': async (req, res) => {
// 接受所有方法
await handleWebhook(req.body)
res.json({ received: true })
},
},
})
```
> 详细文档请参考 [HTTP 路由](/modules/network/http)
## Room 系统