您现在的位置是:网站首页 > RESTful API开发支持文章详情

RESTful API开发支持

RESTful API是一种基于HTTP协议的架构风格,广泛应用于现代Web开发中。Express作为Node.js的轻量级框架,提供了简洁灵活的方式构建RESTful服务。下面从路由设计、状态码处理、数据验证等方面展开具体实现方法。

路由设计规范

RESTful API的核心在于资源导向的路由设计。Express中通过app.METHOD()系列方法实现:

const express = require('express');
const app = express();

// 用户资源路由
app.get('/users', (req, res) => {
  // 获取用户列表
});

app.post('/users', (req, res) => {
  // 创建新用户
});

app.get('/users/:id', (req, res) => {
  // 获取单个用户
});

app.put('/users/:id', (req, res) => {
  // 全量更新用户
});

app.patch('/users/:id', (req, res) => {
  // 部分更新用户
});

app.delete('/users/:id', (req, res) => {
  // 删除用户
});

嵌套资源路由建议不超过两级:

// 文章评论嵌套路由
app.get('/articles/:articleId/comments', (req, res) => {
  const { articleId } = req.params;
  // 获取指定文章的所有评论
});

HTTP状态码应用

正确的状态码能显著提升API可读性。常见场景处理:

// 成功响应
res.status(200).json({ data: user });
res.status(201).json({ created: true });

// 客户端错误
res.status(400).json({ error: '参数校验失败' });
res.status(404).json({ error: '资源不存在' });

// 服务端错误
res.status(500).json({ error: '数据库连接失败' });

特殊状态码的应用示例:

// 204 No Content
app.delete('/posts/:id', (req, res) => {
  deletePost(req.params.id);
  res.status(204).end();
});

// 429 Too Many Requests
app.get('/api', (req, res) => {
  if (rateLimitExceeded(req.ip)) {
    res.status(429).json({
      retryAfter: 60
    });
  }
});

请求数据验证

使用express-validator进行数据校验:

const { body, validationResult } = require('express-validator');

app.post('/users', 
  body('email').isEmail(),
  body('password').isLength({ min: 8 }),
  (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }
    // 处理有效数据
  }
);

复杂校验规则示例:

body('phone').custom(value => {
  if (!/^1[3-9]\d{9}$/.test(value)) {
    throw new Error('手机号格式错误');
  }
  return true;
}),
body('birthday').isISO8601().toDate()

响应数据格式化

统一响应结构增强客户端处理效率:

// 响应包装中间件
function responseWrapper(req, res, next) {
  const originalJson = res.json;
  res.json = function(data) {
    originalJson.call(this, {
      code: res.statusCode,
      timestamp: new Date().toISOString(),
      data: data
    });
  };
  next();
}

// 使用示例
app.get('/products', responseWrapper, (req, res) => {
  res.json(getAllProducts()); // 自动包装
});

分页响应示例:

app.get('/articles', (req, res) => {
  const page = parseInt(req.query.page) || 1;
  const limit = parseInt(req.query.limit) || 10;
  
  const result = getPaginatedArticles(page, limit);
  
  res.json({
    items: result.data,
    pagination: {
      total: result.total,
      page,
      limit,
      totalPages: Math.ceil(result.total / limit)
    }
  });
});

版本控制策略

API版本控制常见实现方式:

  1. URL路径版本控制:
app.get('/v1/users', v1UserController);
app.get('/v2/users', v2UserController);
  1. 请求头版本控制:
app.get('/users', (req, res) => {
  const apiVersion = req.get('X-API-Version') || 'v1';
  if (apiVersion === 'v2') {
    return v2UserController(req, res);
  }
  return v1UserController(req, res);
});
  1. 查询参数版本控制:
app.get('/users', (req, res) => {
  switch(req.query.version) {
    case '2.0': return v2Controller(req, res);
    default: return v1Controller(req, res);
  }
});

认证授权实现

JWT认证完整示例:

const jwt = require('jsonwebtoken');
const expressJwt = require('express-jwt');

// 签发Token
app.post('/login', (req, res) => {
  const user = authenticate(req.body);
  const token = jwt.sign(
    { userId: user.id },
    process.env.SECRET,
    { expiresIn: '2h' }
  );
  res.json({ token });
});

// 保护路由
app.get('/profile',
  expressJwt({ secret: process.env.SECRET }),
  (req, res) => {
    res.json(getUserProfile(req.user.userId));
  }
);

// 错误处理
app.use((err, req, res, next) => {
  if (err.name === 'UnauthorizedError') {
    res.status(401).json({ error: '无效的访问令牌' });
  }
});

性能优化技巧

  1. 路由懒加载:
// 按需加载路由处理器
app.get('/reports', (req, res, next) => {
  require('./handlers/reports')(req, res, next);
});
  1. 响应压缩:
const compression = require('compression');
app.use(compression());
  1. 缓存控制:
app.get('/static-data', (req, res) => {
  res.set('Cache-Control', 'public, max-age=3600');
  res.json(getStaticData());
});

错误处理机制

结构化错误处理中间件:

class APIError extends Error {
  constructor(message, status = 500) {
    super(message);
    this.status = status;
  }
}

// 业务代码中抛出错误
app.get('/orders/:id', (req, res) => {
  const order = getOrder(req.params.id);
  if (!order) throw new APIError('订单不存在', 404);
});

// 全局错误处理器
app.use((err, req, res, next) => {
  const status = err.status || 500;
  res.status(status).json({
    error: {
      message: err.message,
      code: status,
      stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
    }
  });
});

文档自动生成

使用Swagger UI创建API文档:

const swaggerJsdoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');

const options = {
  definition: {
    openapi: '3.0.0',
    info: {
      title: '电商API',
      version: '1.0.0',
    },
  },
  apis: ['./routes/*.js'], // 扫描路由文件
};

const specs = swaggerJsdoc(options);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));

路由注释示例:

/**
 * @swagger
 * /products:
 *   get:
 *     summary: 获取商品列表
 *     parameters:
 *       - in: query
 *         name: category
 *         schema:
 *           type: string
 *     responses:
 *       200:
 *         description: 商品数组
 */
app.get('/products', productController.list);

测试策略

使用Jest进行API测试:

const request = require('supertest');
const app = require('../app');

describe('用户API测试', () => {
  test('创建用户', async () => {
    const res = await request(app)
      .post('/users')
      .send({
        name: '测试用户',
        email: 'test@example.com'
      });
    expect(res.statusCode).toEqual(201);
    expect(res.body).toHaveProperty('id');
  });

  test('无效邮箱检测', async () => {
    const res = await request(app)
      .post('/users')
      .send({ email: 'invalid' });
    expect(res.statusCode).toEqual(400);
  });
});

集成测试示例:

describe('订单流程测试', () => {
  let authToken;
  
  beforeAll(async () => {
    const loginRes = await request(app)
      .post('/login')
      .send({ username: 'test', password: '123456' });
    authToken = loginRes.body.token;
  });

  test('创建订单需要认证', async () => {
    const res = await request(app)
      .post('/orders')
      .set('Authorization', `Bearer ${authToken}`)
      .send({ productId: 1 });
    expect(res.statusCode).toEqual(201);
  });
});

部署注意事项

生产环境配置建议:

// 安全中间件
const helmet = require('helmet');
app.use(helmet());

// 请求限流
const rateLimit = require('express-rate-limit');
app.use('/api/', rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 100
}));

// CORS配置
const cors = require('cors');
app.use(cors({
  origin: ['https://example.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE']
}));

日志记录方案:

const morgan = require('morgan');
const fs = require('fs');
const path = require('path');

// 访问日志
app.use(morgan('combined', {
  stream: fs.createWriteStream(
    path.join(__dirname, 'access.log'),
    { flags: 'a' }
  )
}));

// 错误日志
app.use((err, req, res, next) => {
  fs.appendFileSync('error.log', `${new Date().toISOString()} - ${err.stack}\n`);
  next(err);
});

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步