• 使用 nodejs 中的 http 模块实现几个超实用的工具


    nodejs 方便了我们前端开发者进行一些服务端上的操作,可以进行无缝地衔接。像其他一些后端语言,如 php, golang, java 等,都需要一定的学习成本,而 nodejs 则就是为前端开发者定制的。

    不会骗你

    在 nodejs 中,提供了原生的 http 模块,我们可以利用 http 模块搞几个常用的小工具,能够大大方便我们的工作。

    我在之前从 0 到 1 学习 node(二)之搭建 http 服务器的文章中也讲解过 http 模块,不过我们这里主要是利用 http 模块来打造几个使用的工具。

    1. 一个简单的 HTTP 服务

    使用 nodejs 搭建 http 服务非常地简单,只需要引入 http 模块即可:

    // server.js
    const http = require('http');
    
    const ip = '127.0.0.1';
    const port = 3000;
    
    http
      .createServer((req, res) => {
        res.end('heelo world!');
      })
      .listen(port, ip);
    console.log(`server has started at ${ip}:${port}`);
    

    然后执行 server.js 文件:

    $ node server.js
    

    在控制台就会输出:

    server has started at 127.0.0.1:3000
    

    然后在浏览器上访问http://127.0.0.1:3000,就能看到hello world!的输出。

    如果想在启动时,通过参数指定 IP 或端口。那么我们可以通过process.env来获取命令行中指定的参数:

    // 通过process.env来获取指定的参数
    // 并设置兜底的数据
    const ip = process.env.IP || '127.0.0.1';
    const port = process.env.PORT || 3000;
    

    在执行 sever.js 时,就可以通过参数指定 IP 和端口:

    $ PORT=3030 node app.js
    

    猫咪觉得你很赞

    2. 一个有延迟的请求

    在做开发和调试过程中,经常需要考虑到一个请求或者图片等加载很慢时,应该怎么处理。

    比如有用户反馈某些图片加载很慢,导致页面看起来不正常。那么我就应该针对图片加载慢进行一些处理,可是怎么模拟这个加载很慢的图片呢?

    很多现有的接口,都无法模拟出这种有特殊延迟的情况,这里我们可以使用 http 模块来实现。

    我们在第 1 个 http 服务器的基础上,进行改进。

    res.end()方法会告知服务器当前次响应结束,若没有调用,则一直处于等待状态。

    我们要实现一个有延迟的响应,可以使用 setTimeout 延迟调用 end()方法即可。这里先实现一个有延迟的接口。

    http
      .createServer((req, res) => {
        const index = req.url.indexOf('?');
        if (index >= 0) {
          const query = req.url.slice(index);
          const ss = new URLSearchParams(query);
          const timeout = ss.get('timeout');
          const type = ss.get('type');
          if (timeout && Number(timeout)) {
            return setTimeout(() => {
              if (type === 'json') {
                res.writeHead(200, { 'Content-Type': 'application/json' });
                res.end(JSON.stringify({ code: 0, msg: 'hello world' }));
              } else if (type === 'image') {
                // 输出本地一个图片
              } else {
                res.end(`delay ${timeout}ms response`);
              }
            }, Number(timeout));
          }
        }
        res.end('hello world!');
      })
      .listen(port, ip);
    

    想要延迟输出图片时,需要通过数据流的方式读取本地的图片,然后输出到前端:

    const stream = fs.createReadStream('./img/s.jpg');
    const responseData = []; //存储文件流
    if (stream) {
      //判断状态
      stream.on('data', function (chunk) {
        responseData.push(chunk);
      });
      stream.on('end', function () {
        const finalData = Buffer.concat(responseData);
        // response.write();
        res.writeHead(200, { 'Content-Type': 'image/jpg' });
        res.end(finalData);
      });
    }
    

    3. 实现接口的中转代理

    我们有时会遇到需要的接口存在跨域,或者是内网接口的问题,这时我们就需要通过一个中间层,来对接口进行中转代理,才能正常地访问接口。

    无所谓,也有点累

    3.1 原生 http 模块来实现

    实现接口代理时要注意亮点:

    1. 透传,接收到的所有数据,根据需要尽量都传给代理的接口,例如 cookie,参数等;
    2. 设置跨域头,为了方便前端的访问,我们需要在返回的头部加上 3 个可以跨域的字段;

    跨域的方式有很多种,比如 jsonp 也是其中一种,但 cors 跨域是比较好的一种,前端可以有效地控制请求时间和取消请求。

    在设置跨域头Access-Control-Allow-Origin时,这里是不建议直接设置成*。一方面是不安全,所有的域名都可以访问;再有就是前端不会再传送 cookie,无法进行一些登录态的校验等。

    在设置Access-Control-Allow-Origin之前,我们要先校验下 headers 中的 referer,如果为空或者不满足白名单的要求,则可以直接返回 403。

    const allowList = ['joke.qq.com', 'www.qq.com'];
    if (!req.headers || !req.headers.referer) {
      res.writeHead(403, 'forbidden');
      res.end('403 forbidden');
      return;
    }
    const { hostname } = new URL(req.headers.referer);
    if (!allowList.includes(hostname)) {
      res.writeHead(403, 'forbidden');
      res.end('403 forbidden');
      return;
    }
    

    满足要求之后,需要将 referer 最后的斜杠/去掉,否则会设置不成功。完成的代码样例如下:

    const http = require('http');
    const https = require('https');
    
    const ip = process.env.IP || '127.0.0.1';
    const port = process.env.PORT || 3001;
    
    http
      .createServer((req, res) => {
        const allowList = ['joke.qq.com', 'www.qq.com'];
        if (!req.headers || !req.headers.referer || allow) {
          res.writeHead(403, 'forbidden');
          res.end('403 forbidden');
          return;
        }
        console.log('发起请求', req.headers);
        https
          .get('https://www.v2ex.com/api/topics/latest.json', (response) => {
            let data = '';
            response.on('data', (chunk) => {
              data += chunk;
            });
            response.on('end', () => {
              res.setHeader('Access-Control-Allow-Origin', (req.headers.referer || '').replace(//$/, ''));
              res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
              res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
              res.end(data);
            });
          })
          .on('error', (e) => {
            console.error(`请求遇到问题: ${e.message}`, e);
            res.end('error');
          });
      })
      .listen(port, ip);
    console.log(`server has started at ${ip}:${port}`);
    

    3.2 proxy 代理组件

    若需要代理更多的接口,或者路径是从前端传过来的,我们自己倒是也可以实现,不过还有更方便的 proxy 代理组件了。

    我养你

    这里我们用 http-proxy 组件来实现:

    const http = require('http');
    const httpProxy = require('http-proxy');
    
    const ip = process.env.IP || '127.0.0.1';
    const port = process.env.PORT || 3000;
    
    const proxy = httpProxy.createProxyServer({
      target: 'https://www.v2ex.com', // 代理的接口地址
      changeOrigin: true,
    });
    
    http
      .createServer((req, res) => {
        // 设置跨域头
        res.setHeader('Access-Control-Allow-Origin', (req.headers.referer || '').replace(//$/, ''));
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
        res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
    
        // 将请求和响应的对象传给proxy
        proxy.web(req, res);
      })
      .listen(port, ip);
    

    然后前端直接按照路径发起请求即可:

    axios('http://localhost:3000/api/topics/latest.json').then(console.log).catch(console.error);
    

    4. 模拟数据

    前端在写页面逻辑时,经常要考虑到数据的各种情况,比如无数据时,长列表,各种长度的昵称等。

    头脑发热

    无论是读取配置的 json 文件,还是用代码生成的数据,都不具有随机性。

    现在,我们可以利用 mockjs 来实现各种数据的模拟:

    const http = require('http');
    const Mock = require('mockjs');
    
    const ip = process.env.IP || '127.0.0.1';
    const port = process.env.PORT || 3000;
    
    http
      .createServer((req, res) => {
        const result = Mock.mock({
          code: 0,
          msg: 'success',
          'x-from': 'mock',
          data: Mock.mock({
            'rank|20': [
              {
                'no|+1': 1, // no 字段从 1 开始自增
                uin: () => Mock.Random.string(32), // 32 长度的随机字符串
                nick: () => Mock.Random.string(1, 20), // 长度在 1-20 之间的随机字符串
                face: () => Mock.Random.image('120x120'), // 120*120 的图片
                score: () => Mock.Random.integer(1, 2000), // 分数
              },
            ],
          }),
        });
    
        res.setHeader('Access-Control-Allow-Origin', (req.headers.referer || '').replace(//$/, ''));
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
        res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
        res.writeHead(200, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(result, null, 2));
      })
      .listen(port, ip);
    

    生成的数据:

    mockjs 生成的数据

    关于 Mockjs 更多的语法规则,可以访问https://github.com/nuysoft/Mock/wiki/Getting-Started

    5. 总结

    使用 nodejs 还可以实现更多的功能,这里我们也仅仅是实现了其中简单的几个。基于我们的前端知识和业务需求,我们还能实现更多的小工具。

    点个好看

    也欢迎您关注我的公众号:“前端小茶馆

    前端小茶馆公众号

  • 相关阅读:
    Java数值类型转换
    认识电脑硬件
    ssh(sturts2_spring_hibernate) 框架搭建之spring
    ssh(sturts2_spring_hibernate) 框架搭建之hibernate2
    ssh(sturts2_spring_hibernate) 框架搭建之hibernate1
    SpringAOP之静态代理
    ssh(sturts2_spring_hibernate) 框架搭建之JPA代替hibernate
    struts之动态方法调用使用通配符
    struts之动态方法调用改变表单action属性
    ssh(sturts2_spring_hibernate) 框架搭建之struts2
  • 原文地址:https://www.cnblogs.com/xumengxuan/p/14656565.html
Copyright © 2020-2023  润新知