使用bun快速建立restful服务器-2

33 阅读3分钟

继续添加一些特性

  1. 在main.js上增加代码
import {login} from "./loginService.js";
import { JSONUtils } from './json-utils.js';

const server = Bun.serve({
    port: 3000,
    idleTimeout: 10,
    routes: {
        "/login": {
            async POST(req, server) {
                server.timeout(req, 60);
                
                const cookies = req.cookies;
                console.log(cookies);

                const loginData = await req.json();
                const result = await login(loginData);
                
                return Response.json(result);//这种方法不能设置headers
            }
        },
        "/users/:id" : {//路径上面带参数
            POST: async (req, server) => {
                server.timeout(req, 60);
                const body = await req.json();
                const headers1 = new Headers({ 'Content-Type': 'application/json' });
                headers1.append("key1", "111");
                headers1.set("key2","222");
                console.log(headers1);
                //用内置的response对象,自己json,可以给response加header包括Content-Type
                return new Response(JSONUtils.stringify({ created: true,id: `${req.params.id}`, ...body }), {
                    headers: headers1,
                });
            }
        }
    },
    fetch() {
        return new Response("url都没匹配到,这里兜底"); //同步
    },
});

console.log(`Listening on ${server.url}`);
  1. 添加json-util.js
/**
 * 轻量 JSON 工具库
 * 功能:解析/序列化/格式化/校验/深合并/安全解析
 * 兼容:Node.js + 浏览器
 */
export const JSONUtils = {
  /**
   * 安全解析 JSON(捕获解析错误,避免程序崩溃)
   * @param {string} jsonStr - JSON 字符串
   * @param {*} defaultValue - 解析失败时的默认值
   * @returns {*} 解析结果或默认值
   */
  parse: (jsonStr, defaultValue = null) => {
    if (typeof jsonStr !== 'string') return defaultValue;
    try {
      // 严格模式:禁止解析函数/undefined等非标准JSON
      return JSON.parse(jsonStr, (key, value) => {
        if (typeof value === 'function' || value === undefined) {
          return defaultValue;
        }
        return value;
      });
    } catch (error) {
      console.warn('JSON 解析失败:', error.message);
      return defaultValue;
    }
  },

  /**
   * 序列化 JSON(处理循环引用、特殊值)
   * @param {*} data - 要序列化的数据
   * @param {number} space - 格式化缩进(默认2)
   * @returns {string} JSON 字符串
   */
  stringify: (data, space = 2) => {
    const seen = new WeakSet(); // 处理循环引用
    try {
      return JSON.stringify(
        data,
        (key, value) => {
          // 处理循环引用
          if (typeof value === 'object' && value !== null) {
            if (seen.has(value)) return '[Circular]';
            seen.add(value);
          }
          // 处理特殊值(JSON 默认不支持 undefined/NaN/Infinity)
          if (value === undefined) return null;
          if (typeof value === 'number' && (isNaN(value) || !isFinite(value))) {
            return null;
          }
          return value;
        },
        space
      );
    } catch (error) {
      console.warn('JSON 序列化失败:', error.message);
      return '{}';
    }
  },

  /**
   * 格式化 JSON 字符串(美化输出)
   * @param {string} jsonStr - 原始 JSON 字符串
   * @param {number} space - 缩进空格数(默认2)
   * @returns {string} 格式化后的字符串(失败返回原字符串)
   */
  format: (jsonStr, space = 2) => {
    const parsed = JSONUtils.parse(jsonStr);
    return parsed === null ? jsonStr : JSONUtils.stringify(parsed, space);
  },

  /**
   * 校验是否为合法 JSON 字符串
   * @param {string} jsonStr - 待校验字符串
   * @returns {boolean} 是否合法
   */
  isValid: (jsonStr) => {
    if (typeof jsonStr !== 'string') return false;
    try {
      JSON.parse(jsonStr);
      return true;
    } catch {
      return false;
    }
  },

  /**
   * 深合并多个 JSON 对象(后面对象覆盖前面,不修改原对象)
   * @param  {...object} objs - 待合并的对象列表
   * @returns {object} 合并后的新对象
   */
  deepMerge: (...objs) => {
    const merge = (target, source) => {
      const result = { ...target };
      for (const key in source) {
        if (source.hasOwnProperty(key)) {
          const targetVal = target[key];
          const sourceVal = source[key];
          // 递归合并对象(非数组)
          if (
            typeof targetVal === 'object' &&
            typeof sourceVal === 'object' &&
            !Array.isArray(targetVal) &&
            !Array.isArray(sourceVal) &&
            targetVal !== null &&
            sourceVal !== null
          ) {
            result[key] = merge(targetVal, sourceVal);
          } else {
            // 数组/基本类型直接覆盖
            result[key] = sourceVal;
          }
        }
      }
      return result;
    };

    return objs.reduce((acc, curr) => {
      if (typeof curr !== 'object' || curr === null || Array.isArray(curr)) {
        return acc; // 跳过非对象/数组
      }
      return merge(acc, curr);
    }, {});
  },

  /**
   * 安全解析 JSON(防原型链污染)
   * @param {string} jsonStr - JSON 字符串
   * @returns {*} 解析结果(过滤 __proto__/constructor 等危险字段)
   */
  safeParse: (jsonStr) => {
    const dangerousKeys = ['__proto__', 'constructor', 'prototype'];
    return JSONUtils.parse(jsonStr, null, (key, value) => {
      if (dangerousKeys.includes(key)) {
        return undefined; // 过滤危险字段
      }
      return value;
    });
  },

  /**
   * 压缩 JSON 字符串(移除空格/换行)
   * @param {string} jsonStr - 格式化的 JSON 字符串
   * @returns {string} 压缩后的字符串
   */
  minify: (jsonStr) => {
    const parsed = JSONUtils.parse(jsonStr);
    return parsed === null ? jsonStr : JSON.stringify(parsed);
  }
};

// CommonJS 兼容导出(Node.js 旧版本)
if (typeof module !== 'undefined' && module.exports) {
  module.exports = JSONUtils;
}
  1. 总结
  • "/users/:id"方法展示了
    • 怎么获取路径上的参数
    • 怎么给response加header
  • fetch()方法展示了兜底能力