node 小记

  1. 不支持 import
      node需要使用require
  2. 热更新
      前端开发的时候使用webpack可以很方便地实现热更新,但node我不知如何使用webpack。所以,使用了pm2
pm2 start ./bin/www --watch
  1. 获取请求路径
req.url // 获取的是端口后之后的请求路径
  1. Error: Can’t set headers after they are sent.
      对于一次请求,服务器多次响应就会发生这样的错误。解决方法是,next()send()end()这些方法之前添加return,防止之后的语句对请求做出响应。
  2. url
      这个模块可以让 url 相关操作更简单。
// 比如 url.parse(req.url) 可以得到如下的结果
// 原始的 req.url 是包括参数之类的,但处理之后可以获得pathname 这样纯粹的路径
Url {
   protocol: null,
   slashes: null,
   auth: null,
   host: null,
   port: null,
   hostname: null,
   hash: null,
   search: '?q3',
   query: { q3: '' },
   pathname: '/register/account/login',
   path: '/register/account/login?q3',
   href: '/register/account/login?q3'
}
  1. 跨域
app.all("*",function(req,res,next){
  //设置允许跨域的域名,*代表允许任意域名跨域
  res.header("Access-Control-Allow-Origin","*");
  //允许的header类型
  res.header("Access-Control-Allow-Headers","content-type");
  //跨域允许的请求方式 
  res.header("Access-Control-Allow-Methods","DELETE,PUT,POST,GET,OPTIONS");
  if (req.method.toLowerCase() == 'options')
      res.send(200);  //让options尝试请求快速结束
  else
      next();
})
  1. 请求体太大
      request entity too large这种错误在上传文件的时候可能遇到,比如base64格式的图片。
app.use(express.json({ limit: '50mb' }));
app.use(express.urlencoded({ limit: '50mb', extended: false }));

https://blog.csdn.net/y75475/article/details/86716308

  1. base64 转图片存储
/**
 * 将base64转成图片存储
 * @param {string} path
 * @param {string} base64
 */
const base64ToFile = async (path, base64) => {
  const reg = /^data:image\/\w+;base64,/
  let str = base64.replace(reg, '')
  str = str.replace(/\s/g, '+')
  let dataBuffer = new Buffer(str, 'base64')
  return new Promise((resolve, reject) => {
    fs.writeFile(path, dataBuffer, (err) => {
      if (err) {
        console.log(err)
        resolve({
          status: false,
          errorMsg: err
        })
      } else {
        resolve({
          status: true,
          path
        })
      }
    })
  })
}

/**
 * 分析字符串,如果是base64就按照参数存储
 * @param {string} basePath 基本路径
 * @param {string} fileName 文件名,不包含后缀
 * @param {string} base64 字符串
 */
const strToImageFile = async (basePath, fileName, base64) => {
  const reg = /^data:image\/\w+;base64,/
  return new Promise( async (resolve, reject) => {
    if (reg.test(base64)) {
      const matchs = base64.match(/^(data:image\/(\w+);base64,)/)
      if (matchs) {
        const type = matchs[2]
        let path = basePath + fileName + '.' + type
        const res = await base64ToFile(path, base64)
        resolve(res)
      } else {
        resolve({
          status: false,
          errorMsg: '无效的图片格式'
        })
      }
    } else {
      resolve({
        status: true,
        path: base64
      })
    }
  })
}
  1. 请求内(外)部接口
      碰到需要请求内(外)部接口的情况,可以使用axios(request停止维护了)。使用方法和前端一样,可以用axios.get(xxx).then(),也可以使用await axios.get(xxxx),后端可能偏向后一种方法,但最好配合try……catch……使用。
      请求内部接口的url,我用的是http://localhost:3000/xxx这样的。
  2. app.use()路径使用*/的区别
      具体的我也不清楚。从碰到的问题来看,使用*的话,req.url会变成/,即真实路径会丢失,而使用/则不会丢失。
  3. 获取全部路由
      因为我的鉴权中间件是最先执行的,这时需要知道请求的路径是否存在,否则会统一当作没有权限的接口处理。虽然也没太大影响,但不优雅。而express框架是不会告诉中间件应用的所有路由的。在网上找到的解决方法是解析app._router.stack获取全部路由。
// utils.js
/**
 * 获取全部路由--在 app.js 中使用
 */
const listRoutes = (routes = [], stack, parent) => {

  parent = parent || '';
  stack.forEach(function(r){
    if (r.route && r.route.path){
      var method = '';

      for(method in r.route.methods){
        if(r.route.methods[method]){
          routes.push({method: method.toUpperCase(), path: parent + r.route.path});
        }
      }       

    } else if (r.handle && r.handle.name == 'router') {
      const routerName = r.regexp.source.replace("^\\","").replace("\\/?(?=\\/|$)","");
      return listRoutes(routes, r.handle.stack, parent + routerName);
    }
  });
  return routes;
}

module.exports = {
  listRoutes
}

// app.js
const { listRoutes } = require('./utils/utils')
……
// 获取全部路由--在 app.use 之后执行
global.routes = listRoutes([], app._router.stack)

// 这样,在需要全部路由的中间件里就可以通过 global.routes 获取路由了。
  1. SyntaxError: Cannot use import statement outside a module
      node 早起只支持 CommenJS 模块,v13.2.0 之后才支持 ES6 对模块,不过需要 package.json 里添加 “type”: “module”。但这会导致 ./bin/www 执行报错:ERR_UNKNOWN_FILE_EXTENSION。所以暂时不用 ES6 模块好了。
  2. 写入文件
const fs = require('fs')
// 1. 异步写入 json 文件
// JSON.stringify 的第二三个参数可以让 json 字符串美观一点,不至于压缩在一行。
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
fs.writeFile('file.json', JSON.stringify(data, null, '\t'))

// 2. 同步写入 json 文件
fs.writeFileSync('file.json', JSON.stringify(data, null, '\t'))

// 2. 打开文件再写入
const fd = fs.openSync('file.json', 'w')
fs.writeSync(fd, JSON.stringify(data, null, '\t'))