Skip to content

高级用法

1. 认证和授权

JWT 认证示例

typescript
import jwt from 'jsonwebtoken';

const SECRET = 'your-secret-key';

// 生成 token
function generateToken(userId: number): string {
  return jwt.sign({ userId }, SECRET, { expiresIn: '24h' });
}

// 验证 token
function verifyToken(token: string): { userId: number } | null {
  try {
    return jwt.verify(token, SECRET) as { userId: number };
  } catch {
    return null;
  }
}

// 认证中间件
const authenticate = (req, res, next) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  
  if (!token) {
    return res.status(401).send({ error: 'Missing token' });
  }
  
  const decoded = verifyToken(token);
  if (!decoded) {
    return res.status(401).send({ error: 'Invalid token' });
  }
  
  req.user = decoded;
  next();
};

// 应用中间件
app.use(authenticate);

// 受保护的路由
app.get('/api/profile', (req, res) => {
  res.send({ userId: req.user.userId });
});

// 登录路由
app.post('/api/login', (req, res) => {
  const { username, password } = req.body;
  
  // 验证用户名和密码
  if (username === 'admin' && password === 'password') {
    const token = generateToken(1);
    return res.send({ token });
  }
  
  res.status(401).send({ error: 'Invalid credentials' });
});

客户端使用

typescript
let authToken: string;

// 登录
async function login(username: string, password: string) {
  const response = await client.post<{ token: string }>('/api/login', {
    username,
    password,
  });
  authToken = response.data.token;
}

// 发送认证请求
async function getProfile() {
  const response = await client.get('/api/profile', {
    headers: { Authorization: `Bearer ${authToken}` },
  });
  console.log(response.data);
}

2. 数据库集成

使用 TypeORM

typescript
import { createConnection, Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}

// 初始化数据库
let db;

async function initDatabase() {
  db = await createConnection({
    type: 'sqlite',
    database: 'app.db',
    entities: [User],
    synchronize: true,
  });
}

// 路由
app.get('/api/users/:id', async (req, res) => {
  const user = await db.getRepository(User).findOne(req.params.id);
  
  if (!user) {
    return res.status(404).send({ error: 'User not found' });
  }
  
  res.send(user);
});

app.post('/api/users', async (req, res) => {
  const user = db.getRepository(User).create(req.body);
  await db.getRepository(User).save(user);
  res.status(201).send(user);
});

3. 文件上传处理

处理文件上传

typescript
import fs from 'fs';
import path from 'path';

// 创建上传目录
const uploadDir = path.join(__dirname, 'uploads');
if (!fs.existsSync(uploadDir)) {
  fs.mkdirSync(uploadDir);
}

// 文件上传路由
app.post('/api/upload', (req, res) => {
  const { filename, data } = req.body;
  
  if (!filename || !data) {
    return res.status(400).send({ error: 'Missing filename or data' });
  }
  
  const filepath = path.join(uploadDir, filename);
  
  // 防止路径遍历攻击
  if (!filepath.startsWith(uploadDir)) {
    return res.status(400).send({ error: 'Invalid filename' });
  }
  
  fs.writeFileSync(filepath, Buffer.from(data, 'base64'));
  
  res.status(201).send({
    filename,
    path: `/uploads/${filename}`,
  });
});

// 文件下载路由
app.get('/api/download/:filename', (req, res) => {
  const filepath = path.join(uploadDir, req.params.filename);
  
  if (!filepath.startsWith(uploadDir) || !fs.existsSync(filepath)) {
    return res.status(404).send({ error: 'File not found' });
  }
  
  const data = fs.readFileSync(filepath);
  res.send({
    filename: req.params.filename,
    data: data.toString('base64'),
  });
});

客户端上传文件

typescript
async function uploadFile(file: File) {
  const reader = new FileReader();
  
  reader.onload = async (e) => {
    const data = e.target?.result as string;
    const base64 = data.split(',')[1];
    
    const response = await client.post('/api/upload', {
      filename: file.name,
      data: base64,
    });
    
    console.log('File uploaded:', response.data);
  };
  
  reader.readAsDataURL(file);
}

4. 实时数据同步

使用事件发射器

typescript
import { EventEmitter } from 'events';

const dataEmitter = new EventEmitter();

// 数据变化时发出事件
function updateData(data: any) {
  dataEmitter.emit('data-changed', data);
}

// 轮询获取最新数据
app.get('/api/data/latest', (req, res) => {
  let latestData = null;
  
  const listener = (data) => {
    latestData = data;
  };
  
  dataEmitter.on('data-changed', listener);
  
  // 等待数据变化或超时
  const timeout = setTimeout(() => {
    dataEmitter.removeListener('data-changed', listener);
    res.send({ data: latestData });
  }, 30000); // 30 秒超时
  
  // 如果立即有数据变化
  if (latestData) {
    clearTimeout(timeout);
    dataEmitter.removeListener('data-changed', listener);
    res.send({ data: latestData });
  }
});

5. 错误恢复和重试

客户端重试逻辑

typescript
async function requestWithRetry<T>(
  fn: () => Promise<IResponseObject<T>>,
  maxRetries: number = 3,
  delayMs: number = 1000
): Promise<IResponseObject<T>> {
  let lastError;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      
      // 不重试 4xx 错误
      if (error.statusCode >= 400 && error.statusCode < 500) {
        throw error;
      }
      
      // 等待后重试
      if (i < maxRetries - 1) {
        await new Promise(resolve => setTimeout(resolve, delayMs * (i + 1)));
      }
    }
  }
  
  throw lastError;
}

// 使用
const response = await requestWithRetry(
  () => client.get<User>('/api/users/1'),
  3,
  1000
);

6. 批量操作

批量获取数据

typescript
app.post('/api/users/batch', (req, res) => {
  const { ids } = req.body;
  
  if (!Array.isArray(ids)) {
    return res.status(400).send({ error: 'ids must be an array' });
  }
  
  const users = ids
    .map(id => getUser(id))
    .filter(user => user !== null);
  
  res.send(users);
});

// 客户端
async function getMultipleUsers(ids: number[]) {
  const response = await client.post<User[]>('/api/users/batch', { ids });
  return response.data;
}

7. 缓存策略

实现缓存层

typescript
class CacheManager<T> {
  private cache = new Map<string, { data: T; timestamp: number }>();
  private ttl: number; // 毫秒

  constructor(ttlSeconds: number = 300) {
    this.ttl = ttlSeconds * 1000;
  }

  get(key: string): T | null {
    const item = this.cache.get(key);
    
    if (!item) return null;
    
    if (Date.now() - item.timestamp > this.ttl) {
      this.cache.delete(key);
      return null;
    }
    
    return item.data;
  }

  set(key: string, data: T): void {
    this.cache.set(key, {
      data,
      timestamp: Date.now(),
    });
  }

  clear(): void {
    this.cache.clear();
  }
}

// 使用
const userCache = new CacheManager<User>(300); // 5 分钟缓存

app.get('/api/users/:id', (req, res) => {
  const cacheKey = `user:${req.params.id}`;
  
  // 检查缓存
  const cached = userCache.get(cacheKey);
  if (cached) {
    return res.send(cached);
  }
  
  // 从数据库获取
  const user = getUser(req.params.id);
  if (!user) {
    return res.status(404).send({ error: 'Not found' });
  }
  
  // 存储到缓存
  userCache.set(cacheKey, user);
  res.send(user);
});

8. 性能监控

添加性能指标

typescript
interface RequestMetrics {
  method: string;
  path: string;
  statusCode: number;
  duration: number;
  timestamp: Date;
}

const metrics: RequestMetrics[] = [];

app.use((req, res, next) => {
  const startTime = Date.now();
  
  res.on('finish', () => {
    metrics.push({
      method: req.method,
      path: req.url,
      statusCode: res.statusCode,
      duration: Date.now() - startTime,
      timestamp: new Date(),
    });
  });
  
  next();
});

// 获取性能报告
app.get('/api/metrics', (req, res) => {
  const avgDuration = metrics.reduce((sum, m) => sum + m.duration, 0) / metrics.length;
  const slowRequests = metrics.filter(m => m.duration > 1000);
  
  res.send({
    totalRequests: metrics.length,
    avgDuration,
    slowRequests,
  });
});

9. 版本控制

API 版本管理

typescript
// v1 路由
const v1Router = express.Router();

v1Router.get('/users/:id', (req, res) => {
  res.send({
    id: req.params.id,
    name: 'John',
  });
});

// v2 路由(改进的响应格式)
const v2Router = express.Router();

v2Router.get('/users/:id', (req, res) => {
  res.send({
    data: {
      id: req.params.id,
      name: 'John',
      email: 'john@example.com',
    },
    meta: {
      version: 'v2',
      timestamp: new Date(),
    },
  });
});

// 挂载路由
app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);

10. 优雅关闭

处理应用关闭

typescript
import { app } from 'electron';

let isShuttingDown = false;

// 标记关闭状态
app.on('before-quit', () => {
  isShuttingDown = true;
});

// 拒绝新请求
app.use((req, res, next) => {
  if (isShuttingDown) {
    return res.status(503).send({ error: 'Server is shutting down' });
  }
  next();
});

// 清理资源
app.on('before-quit', async () => {
  // 关闭数据库连接
  if (db) {
    await db.close();
  }
  
  // 移除 IPC 监听器
  ipcServer.removeAllListeners();
  
  // 等待待处理请求完成
  await new Promise(resolve => setTimeout(resolve, 5000));
});

这些高级示例展示了如何在实际应用中使用 ipc-express。根据你的具体需求选择合适的模式。

Released under the MIT License.