node搭建的一个应用在前端项目中的可切换接口的代理服务器

354 阅读3分钟

web项目的编写过程当中,常常会出现前后端进度不一致的情况,就像谈恋爱的两个人如果步调不一致,那么肯定会很累,更有甚者因节奏的不一样导致分手的下场,所以为了避免前后端走到“分手”的这一步,我们需要把前后端解耦开来,可是对于前端来说,你让我自己写假数据行啊,可是模拟不到网络请求这一步,那这个mock的假数据真是“食之无味,弃之可惜”,于是,

Node: "对面的女孩看过来,看过来,看过来~这里的哥哥有点帅"

node挺身而出,帮我们用三两句话就搭建起了一个代理服务器,于是乎,前端:“面包(数据)我有了,阳光(网络请求)我有了,你(后端)最后来给我爱情就好~”,啊,说了一堆废话,还是来看看我们的可切换源数据的代理node服务器是长啥样吧~~

/**
 * @author luojun <https://github.com/Luooojunnn>
 * @desc 本中间服务器可以切换数据源,如需二次改造,请github上提问
*/
const http = require('http')
const url = require('url')
const path = require('path')
const fs = require('fs')
const colors = require('colors')

/**
 * 环境判断
*/
const ENV = process.argv[2] && process.argv[2].split('=')[1]

http.createServer((req, res) => {
  try {
    /**
     * 将读取api文件写在服务程序内,实现动态更新
     * 遇到一个坑,就是node跑在gw根目录导致这里读取文件采用相对路径就会读不到东西
    */
    let apiFile = JSON.parse(fs.readFileSync(`${path.join(__dirname, '../../data/apiManage.json')}`))
    if (url.parse(req.url).pathname !== '/favicon.ico') {
      // 判断请求方式,请求参数
      let reqMethod = req.method === 'GET' ? 'GET' : (req.method === 'POST' ? 'POST' : req.method)

      // 寻址回值
      if (apiFile[url.parse(req.url, true).pathname.substring(1)]) {
        res.setHeader('Access-Control-Allow-Origin', '*')
        res.setHeader('Allow', '*')
        res.setHeader('Access-Control-Allow-Headers', '*')

        if (reqMethod === 'OPTIONS') {
          res.end('')
        }
        // 根据环境变量选择接口
        let apiAdress = apiFile[url.parse(req.url, true).pathname.substring(1)][ENV]
        // TODO 根据环境判断是够需要一个代理转发
        if (ENV === 'dev') {
          console.log('在dev环境'.red)
          // 保存请求参数,用于转发
          let paramsData = ''
          if (reqMethod === 'GET') {
            paramsData = url.parse(req.url, true).search
          } else if (reqMethod === 'POST') {
            let res = ''
            req.on('data', (reqData) => {
              res += reqData
            })
            req.on('end', (reqData) => {
              paramsData = res
              console.log(paramsData)
            })
          }
          console.log(`接口名 ${url.parse(req.url, true).pathname},采用 ${reqMethod} 请求方式,传递的参数是 ${paramsData}`)

          let finalAddress = path.join(__dirname, '../../data/', apiAdress)
          // dev环境 - 读取接口,输出json
          let apiData = fs.readFileSync(finalAddress)
          res.end(apiData.toString('utf8'))
        } else {
          console.log('在测试环境'.red)
          // online环境 - 代理转发 PS: hostname 不含协议
          let hn = url.parse(apiAdress).hostname
          let pt = url.parse(apiAdress).path
          req.headers.host = hn //url.parse(apiAdress).host
          let sRes = res
          if (reqMethod === 'GET') {
            if (url.parse(req.url, true).search) {
              pt = '' + pt + url.parse(req.url, true).search
            }
            console.log(`接口名 ${url.parse(req.url, true).pathname},采用 ${reqMethod} 请求方式,传递的参数是 ${url.parse(req.url, true).search}`)
            HCLIENTFC({
              hostname: hn + '',
              path: pt + '',
              method: reqMethod + '',
              headers: req.headers
            }, (data, statusCode, header) => {
              sRes.writeHead(Number(statusCode), header)
              sRes.end(data)
            }, '')
          } else if (reqMethod === 'POST') {
            let res = ''
            req.on('data', (reqData) => {
              res += reqData
            })
            req.on('end', (reqData) => {
              console.log(`接口名 ${url.parse(req.url, true).pathname},采用 ${reqMethod} 请求方式,传递的参数是 ${res}`)
              /**
               * 将 host 头部信息改成online的域名信息,否则host会指向代码里的请求的localhost:9000
               * !!! 为什么一定要用 header ,因为可以在做爬虫的时候,冒充浏览器端,越过有些代码的机器人识别
               * */
              HCLIENTFC({
                hostname: hn + '',
                path: pt + '',
                method: reqMethod + '',
                headers: req.headers
              }, (data, statusCode, header) => {
                sRes.writeHead(Number(statusCode), header)
                sRes.end(data)
              }, res)
            })
          }
        }
      } else {
        res.writeHead(404)
        res.end('')
      }
    }
  } catch (e) {
    res.end('接口解析出现了一些错误...')
    throw new Error(e)
  }
}).listen(9000, 'localhost', () => {
  console.log('接口服务启动在localhost:9000')
})

/**
 * online服务器
 * @param {Object} obj - 需要参入的配置参数
 * @param {string} obj.hostname - 目标地址ip或域名
 * @param {string} obj.path - 请求路径 (这里忘写导致出问题)
 * @param {string} obj.method - 请求方法
 * @param {Object} obj.headers - 请求头
 * @param {function} callback - 回调
 * @param {string} params - 转发参数
*/
function HCLIENTFC ({ hostname, path = '/', method = 'GET', headers }, callback = () => { }, v = '') {
  let resData = ''
  console.log('接口的信息参数:========1'.blue)
  console.log('hostname:' + hostname)
  console.log('path:' + path)
  console.log('method:' + method)
  console.log(headers)
  console.log('post发送的数据v:' + v)
  console.log('接口的信息参数:--------2'.blue)
  /**
   * 由于发送了header,其accept接受的数据类型可能和返回的数据类型不一致,所以可能导致接口超时,必要时,可去掉发送header
  */
  const HCLIENT = http.request({
    hostname,
    path,
    method,
    headers
  }, (res) => {
    res.setEncoding('utf8')
    res.on('data', (chunk) => {
      resData += chunk
    })
    res.on('end', () => {
      console.log('接口返回结果是:', resData.toString())
      console.log('测试环境返回状态码:', res.statusCode)
      console.log('测试环境返回响应头:', res.headers)
      callback(resData, res.statusCode, res.headers)
    })
  })
  HCLIENT.on('error', (e) => {
    console.log('出现错误,错误信息为:' + e)
    return resData
  })
  HCLIENT.write(v)
  HCLIENT.end()
}


node:"阳光大熊货前来报到!!"

有啥不清楚的可以留言大家一起讨论,共同学习进步!!sir!

git地址:一些好玩的Node

PS: 之前关于get、post请求参数的获取判断有误,线已改正(180927)