使用 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 模块来实现
实现接口代理时要注意亮点:
- 透传,接收到的所有数据,根据需要尽量都传给代理的接口,例如 cookie,参数等;
- 设置跨域头,为了方便前端的访问,我们需要在返回的头部加上 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 更多的语法规则,可以访问https://github.com/nuysoft/Mock/wiki/Getting-Started。
5. 总结
使用 nodejs 还可以实现更多的功能,这里我们也仅仅是实现了其中简单的几个。基于我们的前端知识和业务需求,我们还能实现更多的小工具。
也欢迎您关注我的公众号:“前端小茶馆”
使用 nodejs 中的 http 模块实现几个超实用的工具的更多相关文章
- 介绍nodejs中的path模块的几个方法
webpack中常用的: var path = require('path') 是nodejs中的path模块,介绍一下webpack中常用的几个path模块的方法: 应用node环境的时候,这个pa ...
- nodejs中的fs模块中的方法
nodejs中的fs模块 引入模块 const fs =require("fs") 检测文件是否存在fs.stat(path,callback) fs.stat("./n ...
- 浅析nodeJS中的Crypto模块,包括hash算法,HMAC算法,加密算法知识,SSL协议
node.js的crypto在0.8版本,这个模块的主要功能是加密解密. node利用 OpenSSL库(https://www.openssl.org/source/)来实现它的加密技术, 这是因为 ...
- NodeJS中的http模块
利用http模块,nodejs可以开发服务器, 极大简化服务器的创建: var http = require("http"); //创建服务器 var server = http. ...
- [转]nodejs中的process模块--child_process.exec
1.process是一个全局进程,你可以直接通过process变量直接访问它. process实现了EventEmitter接口,exit方法会在当进程退出的时候执行.因为进程退出之后将不再执行事件循 ...
- 60.浅谈nodejs中的Crypto模块
转自:https://www.cnblogs.com/c-and-unity/articles/4552059.html node.js的crypto在0.8版本并没有改版多少,这个模块的主要功能是加 ...
- nodeJS中使用mongoose模块操作mongodb数据库
在实际运用中,对于数据库的操作我们不可能一直在cmd命令行中进行操作,一般情况下需要在node环境中来操作mongodb数据库,这时就需要引入mongoose模块来对数据库进行增删改查等操作. 首先, ...
- nodejs中的Crypto模块
我是属于实用型的选手,千万别问我过多原理性的东西,我只知道,这个是最好的,我就用它. http://cnodejs.org/topic/504061d7fef591855112bab5
- Node.js学习笔记(二) --- CommonJs和Nodejs 中自定义模块
一. 什么是 CommonJs? JavaScript 是一个强大面向对象语言,它有很多快速高效的解释器. 然而, JavaScript标准定义的 API 是为了构建基于浏览器的应用程序.并没有制定一 ...
随机推荐
- css useful skills blogs
css useful skills blogs https://caniuse.com/ https://css-tricks.com https://css-tricks.com/almanac/p ...
- input support upload excel only
input support upload excel only demo https://codepen.io/xgqfrms/pen/vYONpLB <!-- <input placeh ...
- Dart 编写Api弃用警告
例如body2在以后的版本将被bodyText1代替 @Deprecated( 'This is the term used in the 2014 version of material desig ...
- 听说USDN最近很火,它究竟是什么?
最近USDN在各大社区沸沸扬扬,甚至很多人都说USDN将会打破稳定币市场的格局,那么USDN究竟是怎样的一种稳定币呢?小编今天就帮助大家了解一下,新一代算法型稳定币USDN. USDN是基于公链NGK ...
- 主打开放式金融的Baccarat项目如何开疆拓土?
DeFi在这个夏天迎来了大爆发,像无托管交易.流动性挖矿.保险协议.NFT代币都在今年看到了有别于以往的应用.随着比特币走入主流,DeFi热度下降,不少人都觉得DeFi热潮已死.但事实是,DeFi的总 ...
- django学习-8.django模板继承(block和extends)
1.前言 django模板继承的作用:模板可以用继承的方式来实现复用,减少冗余内容. 一般来说,一个网站里一般存在多个网页的头部和尾部内容都是一致的,我们就可以通过模板继承来实现复用. 父模板用于放置 ...
- Python爬虫_豆瓣电视剧
1 import requests 2 import json 3 import csv 4 5 6 class DoubantvSpider: 7 def __init__(self): 8 # s ...
- 「TcaplusDB知识库」概念(表、键、记录、索引)
TcaplusDB作为一款NoSQL数据库,语法与传统的SQL关系库有所差异.本文将详细介绍TcaplusDB表.记录.索引这三个数据库中常用术语在TcaplusDB中的概念与意义. 术语\概念 ...
- SpringBoot Test 多线程报错:dataSource already closed
1:前言 最近在项目中使用多线程对大任务拆分处理时,进行数据库操作的时候报错了. 业务代码大概是这样的: @Service public calss TestServiceImpl implement ...
- R语言学习4:函数,流程控制,数据框重塑
本系列是一个新的系列,在此系列中,我将和大家共同学习R语言.由于我对R语言的了解也甚少,所以本系列更多以一个学习者的视角来完成. 参考教材:<R语言实战>第二版(Robert I.Kaba ...