matchMock.js 2.74 KB
Newer Older
陈帅's avatar
陈帅 committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
const mockFile = require('./mock/index');
const pathToRegexp = require('path-to-regexp');
const debug = console.log;
const bodyParser = require('body-parser');

const BODY_PARSED_METHODS = ['post', 'put', 'patch'];

function parseKey(key) {
  let method = 'get';
  let path = key;
  if (key.indexOf(' ') > -1) {
    const splited = key.split(' ');
    method = splited[0].toLowerCase();
    path = splited[1]; // eslint-disable-line
  }
  return {
    method,
    path,
  };
}

function createHandler(method, path, handler) {
  return function(req, res, next) {
    if (BODY_PARSED_METHODS.includes(method)) {
      bodyParser.json({ limit: '5mb', strict: false })(req, res, () => {
        bodyParser.urlencoded({ limit: '5mb', extended: true })(req, res, () => {
          sendData();
        });
      });
    } else {
      sendData();
    }

    function sendData() {
      if (typeof handler === 'function') {
        handler(req, res, next);
      } else {
        res.json(handler);
      }
    }
  };
}

function normalizeConfig(config) {
  return Object.keys(config).reduce((memo, key) => {
    const handler = config[key];
    const { method, path } = parseKey(key);
    const keys = [];
    const re = pathToRegexp(path, keys);
    memo.push({
      method,
      path,
      re,
      keys,
      handler: createHandler(method, path, handler),
    });
    return memo;
  }, []);
}

const mockData = normalizeConfig(mockFile);

function matchMock(req) {
  const { path: exceptPath } = req;
  const exceptMethod = req.method.toLowerCase();
  for (const mock of mockData) {
    const { method, re, keys } = mock;
    if (method === exceptMethod) {
      const match = re.exec(req.path);
      if (match) {
        const params = {};

        for (let i = 1; i < match.length; i = i + 1) {
          const key = keys[i - 1];
          const prop = key.name;
          const val = decodeParam(match[i]);

          if (val !== undefined || !hasOwnProperty.call(params, prop)) {
            params[prop] = val;
          }
        }
        req.params = params;
        return mock;
      }
    }
  }

  function decodeParam(val) {
    if (typeof val !== 'string' || val.length === 0) {
      return val;
    }

    try {
      return decodeURIComponent(val);
    } catch (err) {
      if (err instanceof URIError) {
        err.message = `Failed to decode param ' ${val} '`;
        err.status = err.statusCode = 400;
      }

      throw err;
    }
  }

  return mockData.filter(({ method, re }) => {
    return method === exceptMethod && re.test(exceptPath);
  })[0];
}
module.exports = (req, res, next) => {
  const match = matchMock(req);
  if (match) {
    debug(`mock matched: [${match.method}] ${match.path}`);
    return match.handler(req, res, next);
  } else {
    return next();
  }
};