- 写在前面:官方文档(英文版):https://nodejs.org/dist/latest-v16.x/docs/api/url.html,中文版要收费
1.url
为了方便观察,我们这里使用log4js第三方库来进行日志输入,而不是console.log,详情可以查看官网教程
npm i log4js -D
const log4js = require('log4js')
log4js.configure({
appenders: { cheese: { type: "file", filename: "cheese.log" } },
categories: { default: { appenders: ["cheese"], level: "error" } },
});
var logger = log4js.getLogger('cheese');
logger.level = "debug";
logger.debug('Hello world')
日志输入会在同级目录下的cheese.log文件中,然后在此基础上我们讲解一下url内置模块的作用
app.js
// 导入url模块
const url = require('url')
const log4js = require('log4js')
log4js.configure({
appenders: { cheese: { type: "file", filename: "cheese.log" } },
categories: { default: { appenders: ["cheese"], level: "error" } },
});
var logger = log4js.getLogger('cheese');
const urlString = 'http://www.baidu.com:443/path/index.html?id=2#tag=3'
logger.level = "debug";
//parse解析路径
logger.debug(url.parse(urlString));
const urlObj = {
protocol: 'http:',
slashes: true,
auth: null,
host: 'www.baidu.com:443',
port: '443',
hostname: 'www.baidu.com',
hash: '#tag=3',
search: '?id=2',
query: 'id=2',
pathname: '/path/index.html',
path: '/path/index.html?id=2',
href: 'http://www.baidu.com:443/path/index.html?id=2#tag=3'
}
//format逆向解析
logger.debug(url.format(urlObj))
//resolve进行解析 ../表示返回上一级
logger.debug(url.resolve('http://www.abc.com/a', '../'))
// /b表示从根路径开始找到b文件夹
logger.debug(url.resolve('http://www.abc.com/a', '/b'))
//URLSearchParam类获取参数
const urlParams = new URLSearchParams(url.parse(urlString).search)
logger.debug(urlParams)
logger.debug(urlParams.get('id'))
1.parse
url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
const url = require('url')
const urlString = 'https://www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110'
const parsedStr = url.parse(urlString)
console.log(parsedStr)
2.format
url.format(urlObject)
const url = require('url')
const urlObject = {
protocol: 'https:',
slashes: true,
auth: null,
host: 'www.baidu.com:443',
port: '443',
hostname: 'www.baidu.com',
hash: '#tag=110',
search: '?id=8&name=mouse',
query: { id: '8', name: 'mouse' },
pathname: '/ad/index.html',
path: '/ad/index.html?id=8&name=mouse',
href: 'https://www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110'
}
const parsedObj = url.format(urlObject)
console.log(parsedObj)
3.resolve
url.resolve(from, to)
const url = require('url')
var a = url.resolve('/one/two/three', 'four')
var b = url.resolve('http://example.com/', '/one')
var c = url.resolve('http://example.com/one', '/two')
console.log(a + "," + b + "," + c)
2.querystring
在这里我们使用log4js采用自己封装的工具
log.js
const log4js = require('log4js')
log4js.configure({
appenders: { cheese: { type: "file", filename: "cheese.log" } },
categories: { default: { appenders: ["cheese"], level: "error" } },
});
var logger = log4js.getLogger('cheese');
logger.level = "debug";
// logger.debug('Hello world')
module.exports = logger
然后这一段我也是一脸懵逼,千锋课程删除了前面大部分,只留下了这些代码,额,参照官网看一下吧…不过好在这是一个过时方法。
app.js
const logger = require('../utils/log')
const querystring = require('querystring')
const query = 'id=2&name=tongyi&from=北京'
const query2 = 'id:2&name:tongyi&from:北京'
const queryEscape = 'id%3D2%26name%3Dtongyi%26from%3D%E5%8C%97%E4%BA%AC'
const queryObj = { id: '2', name: 'tongyi', from: '北京' }
logger.debug(querystring.parse(query))
logger.debug(querystring.escape(query))
logger.debug(querystring.unescape(queryEscape))
logger.debug(querystring.stringify(queryObj, ':', '/'))
logger.debug(querystring.parse(query2, '/', ':'))
const newQuery = querystring.stringify(queryObj, null, null, {
encodeURIComponent(string) {
//unescape解码操作,默认将我们的中文字符转换为querystring之后会变成unicode编码
return querystring.unescape(string)
}
})
logger.debug(newQuery)
- 该
node:querystring
模块提供了用于解析和格式化 URL 查询字符串的实用程序。可以使用以下方式访问它:
const querystring = require('node:querystring');
该querystring
API 被视为旧版。虽然它仍在维护,但新代码应该使用<URLSraechParams> API。
1.parse
querystring.parse(str[, sep[, eq[, options]]])
const querystring = require('querystring')
var qs = 'x=3&y=4'
var parsed = querystring.parse(qs)
console.log(parsed)
2.stringify
querystring.stringify(obj[, sep[, eq[, options]]])
const querystring = require('querystring')
var qo = {
x: 3,
y: 4
}
var parsed = querystring.stringify(qo)
console.log(parsed)
3.escape/unescape
querystring.escape(str)
const querystring = require('querystring')
var str = 'id=3&city=北京&url=https://www.baidu.com'
var escaped = querystring.escape(str)
console.log(escaped)
querystring.unescape(str)
const querystring = require('querystring')
var str = 'id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2Fwww.baidu.com'
var unescaped = querystring.unescape(str)
console.log(unescaped)
3.http(s)
server.js
const http = require('http')
const logger = require('../utils/log')
//创建一个服务器
//参数为一个回调函数,说明这是一个高阶函数
const server = http.createServer((request, response) => {
logger.debug(request)
logger.debug(response)
})
//服务器的监听窗口
server.listen(8080, () => {
console.log('server at http://localhost:8080...')
})
1.开启调试模式
node --inspect server.js
- 在浏览器窗口输入:
chrmoe://inspect/#/devices
,不同浏览器前缀可能不一样,比如说360浏览器是se开头,这里使用谷歌浏览器
- 点击左上角绿色的node.js图标计入调试界面
- 如果您的浏览器没有进行连接跳转而是出现以下界面:
- 则需要自己添加网络连接
- 然后切换到控制台进行调试
- 打断点调试(目的是查看当前所有信息)
node --inspect --inspect-brk server.js
2.Node进程管理工具
- supervisor
- nodemon
- forever
- pm2
nodemon的使用:
nodemon server.js
- 作用:保存后可以直接在浏览器运行,不用每次退出后重启服务器
const http = require('http')
const logger = require('../utils/log')
//创建一个服务器
//参数为一个回调函数,说明这是一个高阶函数
const server = http.createServer((request, response) => {
//request知识点
//获取请求地址
const url = request.url
console.log(url)
//response知识点
//第一个参数是状态码,可以不为200,欺骗浏览器
response.writeHead(200, {
//应用程序/普通文本,打开页面后就会自动下载
// 'content-type': 'application/plain'
//纯文本(普通文本)
// 'content-type': 'text/plain'
//html
// 'content-type': 'text/html'
//json
'content-type': 'application/json;charset=utf-8'
})
//默认的标头是可以识别html标签的,即默认就是html
response.write('{"x":100}}')
//表示结束函数,说明已经写完数据
response.end()
})
//服务器的监听窗口
server.listen(8080, () => {
console.log('server at http://localhost:8080...')
})
- 测试:使用postman或者Insomnia进行数据请求
3.Insomina工具的使用
1.发送表单
server.js
const http = require('http')
const logger = require('../utils/log')
const querystring = require('querystring')
const server = http.createServer((request, response) => {
const url = request.url
let data = ''
//接受参数
//chunk是返回的数据流
request.on('data', (chunk) => {
data += chunk
})
request.on('end', () => {
response.writeHead(200, {
'content-type': 'application/json;charset=utf-8'
})
// response.write(`{"url":${url}}`)
response.write(JSON.stringify(querystring.parse(data)))
response.end()
})
})
server.listen(8080, () => {
console.log('server at http://localhost:8080...')
})
2.Insomina发送请求
4.GET请求
server.js
const http = require('http')
const logger = require('../utils/log')
const querystring = require('querystring')
const https = require('https')
const server = http.createServer((request, response) => {
// logger.debug(https.get('https://m.xiaomiyoupin.com/mtop/mf/resource/data/batchList'))
// 第一个参数是请求地址,第二个是回调函数
//通过get请求获取到对象然后向前端返回数据,某种意义上实现了跨域访问
https.get('https://m.xiaomiyoupin.com/mtop/mf/resource/data/batchList', (result) => {
let data = ''
result.on('data', (chunk) => {
data += chunk
})
result.on('end', () => {
response.writeHead(200, {
'content-type': 'application/json;charset=utf-8'
})
response.write(data)
response.end()
})
})
})
server.listen(8080, () => {
console.log('server at http://localhost:8080...')
})
5.POST请求
-
服务器提交(攻击)
-
为了演示服务器攻击,我们首先需要建造一个伪服务器(express服务器,需要事先全局安装好express:
npm i -g express-generator
+npm i -g express
,注意是安装两个第三方库,而不是一个!):- ①创建一个项目文件夹:train-express-env
- ②在终端输入
express -e
,这个之后会有关讲解,目前只是为了演示 - ③安装依赖:
npm i
- ④启动服务器:
npm start
- ⑤在浏览器中输入地址:http://localhost:3000 ,这是默认express服务器的端口号
-
然后我们就可以愉快的测试(攻击)这个服务器了
-
服务器准备工作(创建服务器的业务中间件)
- 创建工具包和工具log.js,请移步2.querystring的知识点,并找到该工具js
- 在routes下的index.js中添加中间件
var express = require('express'); var router = express.Router(); const logger = require('../utils/log') /* GET home page. */ router.get('/', function (req, res, next) { res.render('index', { title: 'Express' }); }); //测试要用到的中间件,模拟服务器的业务,就是被攻击时要做的事情 //我们模拟该业务为创建日志文件,并且不断追加内容,实际上还是不能够体现业务量的大 router.post('/data', (req, res, next) => { logger.debug(req.body) res.send('ok') }) module.exports = router;
-
重启服务器
-
测试:
-
为了方便我们还可以在package.json里面修改start脚本:
"start": "nodemon ./bin/www"
- POST请求
①server.js
const https = require('https')
const http = require('http')
const querystring = require('querystring')
//提交的数据
//首先要封装data,生成querystring
const postData = querystring.stringify({
province: '上海',
city: '上海',
district: '宝山区',
address: '同济支路199号智慧七立方3号楼2-4层',
latitude: 43.0,
longitude: 160.0,
message: '求购一条小鱼',
contact: '13666666',
type: 'sell',
time: 1571217561
})
//请求参数
const option = {
//协议,注意要冒号
protocol: 'http:',
//地址
hostname: 'localhost',
//请求方式
method: 'POST',
//端口号
port: 3000,
//路径
path: '/data',
//请求头
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': Buffer.byteLength(postData)
}
}
const server = http.createServer((req, res) => {
const request = http.request(option, (result) => {
})
//发送请求
request.write(postData)
request.end()
//返回值
res.end()
})
server.listen(8080, () => {
console.log('server at http://localhost:8080')
})
②运行
nodemon server.js
③发送请求
④观察服务器日志
4.跨域
1.jsonp
-
全称jsonpadding
-
CDN网站:https://www.bootcdn.cn/ ,当你不想下载js文件而想调用官方源的时候就可以去这个网站上面找
-
server.js
- 运行:
nodemon server.js
- 运行:
const url = require('url')
const http = require('http')
const server = http.createServer((req, res) => {
let urlstr = req.url
//解析成为对象,添加true获取请求的参数
let urlObj = url.parse(urlstr, true)
console.log(urlObj)
switch (urlObj.pathname) {
case '/api/data':
//调用回调函数,用引号包裹,所以称之为json padding
res.write(`${urlObj.query.cb}('hello')`)
break
default:
res.write('404 page not found')
}
res.end()
})
server.listen(8080, () => {
console.log('server at http://localhost:8080')
})
- index.html
- 运行:
npx http-server -p 9000
- 运行:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
function getData(data) {
console.log(data)
}
</script>
<script src="http://localhost:8080/api/data?cb=getData"></script>
</body>
</html>
2.CORS
-
中文:跨源资源共享
-
重点:
'Access-Control-Allow-Origin': '*'
-
server.js
- 启动服务器:
nodemon server.js
- 启动服务器:
const url = require('url')
const http = require('http')
const server = http.createServer((req, res) => {
let urlstr = req.url
//解析成为对象,添加true获取请求的参数
let urlObj = url.parse(urlstr, true)
console.log(urlObj)
switch (urlObj.pathname) {
case '/api/data':
res.writeHead(200, {
'content-type': 'application/json',
'Access-Control-Allow-Origin': '*'
})
res.write('{"ret":true,"data":"hello"}')
break
default:
res.write('404 page not found')
}
res.end()
})
server.listen(8080, () => {
console.log('server at http://localhost:8080')
})
- index.html
- 运行服务器:
npx http-server -p 9000
- 运行服务器:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//浏览器自带的ajax请求
fetch('http://localhost:8080/api/data')
.then(response => response.json())
.then(result => {
console.log(result)
})
</script>
</body>
</html>
3.middleware(http-proxy-middware)
-
中间件,http代理中间件方式进行跨域访问
-
在npmjs官网查找用法
-
安装中间件:
npm i http-proxy-middleware -S
-
server.js
const http = require('http')
const url = require('url')
//导入中间件
const { createProxyMiddleware } = require('http-proxy-middleware')
const server = http.createServer((req, res) => {
urlStr = req.url
if (/\/ajax/.test(urlStr)) {
// console.log(urlStr)
const proxy = createProxyMiddleware('/ajax', {
//目标网站
target: 'https://mapi.vip.com',
//是否切换源
changeOrigin: true,
//重写路径
pathRewrite: {
'^/ajax': ''
}
})
//开启代理
proxy(req, res)
} else if (/\/api/.test(urlStr)) {
const proxy2 = createProxyMiddleware('/api', {
//目标网站2
target: 'https://m.lagou.com',
//是否切换源
changeOrigin: true,
})
//开启代理
proxy2(req, res)
}
else {
console.log('error')
}
})
server.listen(8080, () => {
console.log('server at http://localhost:8080')
})
5.爬虫
-
cheerio–为服务器特别定制的,快速、灵活、实施的jQuery核心实现.github官网:https://github.com/cheeriojs/cheerio/wiki/Chinese-README
-
安装
npm i cheerio
-
server.js
const http = require('http')
const https = require('https')
const cheerio = require('cheerio')
function filterData(data) {
//2.通过cheerio转换为虚拟DOM方便操作
const $ = cheerio.load(data)
// console.log(data)
//3.进行有关操作
$('.section-item-box p').each((index, el) => {
console.log(index)
console.log($(el).text())
})
}
const server = http.createServer((req, res) => {
let data = ''
//1.首先通过https请求获取到页面
https.get('https://www.meizu.com', (result) => {
result.on('data', (chunk) => {
data += chunk
})
result.on('end', () => {
filterData(data)
})
})
})
server.listen(8080, () => {
console.log('server at http://localhost:8080')
})
6.event
- node.js的事件,与jquery类似
app.js
const EventEmitter = require('events')
class MyEventEmitter extends EventEmitter { }
const event = new MyEventEmitter()
//事件名,事件所有的参数
event.on('play', (value) => {
console.log(value)
})
//事件可以重名,都会触发
event.on('play', (value) => {
console.log(value)
})
event.on('play2', (value) => {
console.log(value)
})
//触发事件,返回值
event.emit('play', 'movie')
event.emit('play2', 'tv')
//再次触发
event.emit('play2', 'tv')
7.File System
app.js
const fs = require('fs')
const fsP = require('fs').promises
// 创建文件夹,它的回调函数是错误优先的(指的是第一个参数必定是err)
fs.mkdir('./logs', (err) => {
//如果有错误就直接抛出
if (err) throw err
console.log('文件夹创建成功')
})
// 文件夹改名
fs.rename('./logs', './log', () => {
console.log('文件夹修改名字成功')
})
// 删除文件夹
fs.rmdir('./log', () => {
console.log('文件夹删除成功')
})
// 写内容到文件里
fs.writeFile(
//要书写内容的文件
'./logs/log1.txt',
//要书写的内容
'hello',
// 错误优先的回调函数
(err) => {
if (err) {
console.log(err.message)
} else {
console.log('文件创建成功')
}
}
)
// 给文件追加内容
fs.appendFile('./logs/log1.txt', '\nworld', () => {
console.log('done.')
})
// 读取文件内容
fs.readFile('./logs/log1.txt', 'utf-8', (err, data) => {
console.log(data)
})
// 删除文件
fs.unlink('./logs/log1.txt', (err) => {
console.log('done.')
})
// 批量写文件
for (var i = 0; i < 10; i++) {
fs.writeFile(`./logs/log-${i}.txt`, `log-${i}`, (err) => {
console.log('done.')
})
}
// 读取文件/目录信息
fs.readdir('./', (err, data) => {
data.forEach((value, index) => {
fs.stat(`./${value}`, (err, stats) => {
// console.log(value + ':' + stats.size)
console.log(value + ' is ' + (stats.isDirectory() ? 'directory' : 'file'))
})
})
})
// 同步读取文件
try {
const content = fs.readFileSync('./logs/log-1.txt', 'utf-8')
console.log(content)
console.log(0)
} catch (e) {
console.log(e.message)
}
console.log(1)
// 异步读取文件:方法一
//读取到的content实际上是buffer流,所以要加转码utf-8,或者不加,使用toString方法
fs.readFile('./logs/log-0.txt', 'utf-8', (err, content) => {
console.log(content)
console.log(0)
})
console.log(1)
// 异步读取文件:方法二
fs.readFile('./logs/log-0.txt', 'utf-8').then(result => {
console.log(result)
})
// 异步读取文件:方法三
function getFile() {
return new Promise((resolve) => {
fs.readFile('./logs/log-0.txt', 'utf-8', (err, data) => {
resolve(data)
})
})
}
//小括号或者中括号作为开头前面一般要加分号
; (async () => {
console.log(await getFile())
})()
// 异步读取文件:方法四
const fsp = fsP.readFile('./logs/log-1.txt', 'utf-8').then((result) => {
console.log(result)
})
console.log(fsP)
// watch 监测文件变化,和删除,除了新建
fs.watch('./logs/log-0.txt', () => {
console.log(0)
})
8.流与zlib
1.stream
- 基本上所有的读写文件操作实际上都是流的操作,该段代码已失效(2022.8.19),请查看官网最新API
const fs = require('fs')
const readstream = fs.createReadStream('./note.txt')
const writestream = fs.createWriteStream('./note2.txt')
writestream.write(readstream)
2.zlib
- 对文件进行压缩,这个很重要
const fs = require('fs')
const zlib = require('zlib')
//做gzip压缩
const gzip = zlib.createGzip()
const readstream = fs.createReadStream('./note.txt')
const writestream = fs.createWriteStream('./note2.gzip')
//const writestream = fs.createWriteStream('./note2.txt')
readstream
//pipe管道操作,以下两个操作要分别进行
.pipe(gzip)
//相当于直接进行拷贝
.pipe(writestream)
//writestream.write(readstream)
9.readline
const readline = require('readline');
const rl = readline.createInterface({
//标准输入流
input: process.stdin,
//标准输出流
output: process.stdout
});
rl.question('What do you think of Node.js? ', (answer) => {
// TODO: Log the answer in a database
console.log(`Thank you for your valuable feedback: ${answer}`);
rl.close();
});
10.Crypto
const crypto = require('crypto')
const password = 'abc123'
const hash = crypto
// 使用sha1加密方式
// .createHash('sha1')
//使用MD5加密方式
.createHash('md5')
//如果有中文就要加utf-8,没有就不用
.update(password, 'utf-8')
//使用十六进制
.digest('hex')
//加密后的密码位数一样
console.log(hash)