nodeJs基础笔记
title: nodeJs基础笔记
date: 2023-11-18 22:33:54
tags:
1. Buffer
1. 概念
Buffer 是一个类似于数组的 对象 ,用于表示固定长度的字节序列。
Buffer 本质是一段内存空间,专门用来处理 二进制数据 。
2. 特点
- Buffer 大小固定且无法调整
- Buffer 性能较好,可以直接对计算机内存进行操作
- 每个元素的大小为 1 字节(byte)
3. 使用
- 创建 Buffer
Node.js 中创建 Buffer 的方式主要如下几种:
- Buffer.alloc
- Buffer.allocUnsafe
- Buffer.from
//创建了一个长度为 10 字节的 Buffer,相当于申请了 10 字节的内存空间,每个字节的值为 0
let buf_1 = Buffer.alloc(10); // 结果为 <Buffer 00 00 00 00 00 00 00 00 00 00>
//创建了一个长度为 10 字节的 Buffer,buffer 中可能存在旧的数据, 可能会影响执行结果,所以叫unsafe
let buf_2 = Buffer.allocUnsafe(10);
//通过字符串创建 Buffer
let buf_3 = Buffer.from('hello');
//通过数组创建 Buffer
let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]);
- Buffer 与字符串的转化
我们可以借助toString
方法将Buffer
转为字符串。toString
默认是按照utf-8
编码方式进行转换的。
let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]);
console.log(buf_4.toString())
- Buffer 的读写
Buffer 可以直接通过 [] 的方式对数据进行处理。
//读取
console.log(buf_3[1]);
//修改
buf_3[1] = 97;
//查看字符串结果
console.log(buf_3.toString());
注意:
- 如果修改的数值超过 255 ,则超过 8 位数据会被舍弃
- 一个 utf-8 的中文字符 一般 占 3 个字节
2. fs模块(file system)
1. 文件写入 (当需要持久化保存数据的时候,应该想到文件写入)
- 同步写入
fs.writeFileSync(文件名,待写入的数据[,选项设置 可选])
- 异步写入
fs.writeFile(文件名,待写入的数据[,选项设置 可选],回调函数)
该函数的第三个参数设置为{flag: 'a'}
可以实现追加写入
- 如果文件不存在,会自动创建文件
- 当写入完成,会自动调用执行函数
- 以下代码中,当写入成功 err: null ;当写入失败 err的值是一个错误对象
const fs = require('fs');
fs.writeFile('./座右铭.txt', '三人行,则必有我师', err=>{if (err) {console.log('写入失败');return;}console.log('写入成功', err); // 写入成功 null
})
- fs追加写入
fs.appendFile(文件名, 待写入数据[, options], 回调)
fs.appendFileSync(文件名, 待写入数据[, options])
// 1. 引入fs模块
const fs = require('fs');
// 2. 调用方法
fs.appendFile('./座右铭.txt', ',则其善者而从之,其不善者而改之', err => {if (err) {console.log('追加失败');return;}console.log('追加成功');
});// fs 中使用 \r\n 实现换行
fs.appendFileSync('./座右铭.txt', '\r\n温故而知新,可以为师矣');// writeFile 可以实现追加写入,将第三个参数设置为: {flag: 'a'}
fs.writeFile('./座右铭.txt', '\r\n一闪一闪亮晶晶', { flag: 'a' }, err => {if (err) {console.log('追加失败');return;}console.log('追加成功');
});
- 流式写入
- 语法:
fs.createWriteStream(path[, options])
- 参数说明:
path 文件路径 ;options 选项配置( 可选 )
// 流式写入 适合 大文件写入或者频繁写入,writeFile 适合写入频率较低的场景
// 1. 导入 fs
const fs = require('fs');
// 2. 创建写入流对象 fs.createWriteCream(文件[,option])
const ws = fs.createWriteStream('./静夜思.txt');
// 3. write() 写入
ws.write('床前明月光\r\n');
ws.write('疑是地上霜\r\n');
ws.write('举头望明月\r\n');
ws.write('低头思故乡\r\n');
// 4. 关闭通道
ws.close(); // close() 是可选的,可以不加
2. 文件读取
- 异步读取
readFile(文件[,option],回调函数(a, b))
,回调函数两个参数。 - 同步读取
fs.readFileSync(path[, options])
// 需求:读取文件静夜思.txt的内容,并输出
// 1. 引入fs模块
const fs = require('fs');
// 2. 异步读取 readFile(文件[,option],回调函数(a, b)) 回调函数两个参数
fs.readFile('./静夜思.txt', (err, data) => {if (err) {console.log('读取失败');return;}console.log(data);console.log(data.toString());
});
// 3. 同步读取 readFileSync()
let data = fs.readFileSync('./静夜思.txt');
console.log(data);
console.log(data.toString());
- 流式读取
fs.createReadStream(path[, options])
// createReadStream 大文件,提高效率
// 1. 引入 fs 模块
const fs = require('fs');
// 2. 创建读取流对象
const rs = fs.createReadStream('./笑看风云.mp4');
// 3. 绑定 data 事件 ,每次取出 64k 数据后执行一次 data 回调
rs.on('data', data => {console.log(data);console.log(data.length); // 65536字节 => 64KB
});
// end 事件,可选事件,读取完毕后, 执行 end 回调
rs.on('end', () => {console.log('读取完成');
});
- 练习-复制文件
// 需求: 复制 笑看风云.mp4
const fs = require('fs');
const process = require('process');
// (1)readFile
// 读取文件内容
// const data = fs.readFileSync('笑看风云.mp4');
// 写入文件
// fs.writeFileSync('./笑看风云2.mp4', data);
// console.log(process.memoryUsage()); // rss: 112545792字节
// (2)createReadStream 流式
// 创建读取流对象
const rs = fs.createReadStream('笑看风云.mp4');
// 创建写入流对象
const ws = fs.createWriteStream('./笑看风云3.mp4');
// 绑定 data 事件
rs.on('data', chunk => {ws.write(chunk);
});
rs.on('end', () => {console.log(process.memoryUsage()); // 获得代码运行的内存占用量。rss: 50831360字节
});// rs.pipe(ws); // 也是文件复制
3. 文件的重命名与移动
- 语法
fs.rename(oldPath, newPath, callback)
fs.renameSync(oldPath, newPath)
- 示例
// 重命名、文件移动,都是改变了 文件的路径
// (1) fs.rename(old, new, callback())
const fs = require('fs');
fs.rename('座右铭.txt', './论语.txt', err => {if (err) {console.log('重命名失败');return;}console.log('重命名成功');
});// 文件的移动
fs.rename('data.txt', '资料/data.txt', err => {if (err) {console.log('文件移动失败');return;}console.log('文件移动成功');
});// (2) fs.renameSync(old, new)
const fs = require('fs');
fs.renameSync('论语.txt', '座右铭.txt');
4. 文件的删除
fs.unlink(path, callback)
、fs.unlinkSync(path)
同步fs.rm(path, callback)
、fs.rmSync(path)
// 1. unlink(path, callback()) unlinkSync(path) 同步
const fs = require('fs');
fs.unlink('./静夜思.txt', err => {if (err) {console.log('删除失败');return;}console.log('删除成功');
});
// 2. rm 方法 fs.rm(path, callback()) rmSync(path) 同步
fs.rm('./座右铭.txt', err => {if (err) {console.log('删除失败');return;}console.log('删除成功');
})
5. 文件夹操作
- 创建文件夹
fs.mkdir(path[, options], callback)
fs.mkdirSync(path[, options])
// 创建文件夹 fs.mkdir(path[, options], callback) m make dir directory fs.mkdirSync(path[, options])同步
fs.mkdir('./html', err => {if (err) {console.log('创建失败');return;}console.log('创建成功');
});// 递归创建
fs.mkdir('./a/b/c',{recursive: true}, err => {if (err) {console.log('创建失败');return;}console.log('创建成功');
});
- 读取文件夹
fs.readdir(path[, options], callback)
fs.readdirSync(path[, options])
fs.readdir('./资料', (err, data) => {if (err) {console.log('读取失败');return;}console.log(data);
});
fs.readdir('./', (err, data) => {if (err) {console.log('读取失败');return;}console.log(data);
});
- 删除文件夹
fs.rmdir(path[, options], callback)
fs.rmdirSync(path[, options])
// 删除文件夹 fs.rmdir(path[, options], callback) fs.rmdirSync(path[, options])
fs.rmdir('./html', err => {if (err) {console.log('删除失败');return;}console.log('删除成功');
});// 递归删除
fs.rmdir('./a', {recursive: true}, err => { // 不推荐使用if (err) {console.log('删除失败');return;}console.log('删除成功');
});fs.rm('./a', { recursive: true }, err => {if (err) {console.log('删除失败');return;}console.log('删除成功');
});
6. 查看资源状态
fs.stat(path[, options], callback)
fs.statSync(path[, options])
//异步获取状态
const fs = require('fs');
fs.stat('./资料/笑看风云.mp4', (err, data) => {if (err) {console.log('操作失败');return;}console.log(data);// isFile 文件console.log(data.isFile()); // true// isDirectory() 文件夹console.log(data.isDirectory()); // false
});
//同步获取状态
let data = fs.statSync('./data.txt');
7. 相对路径和绝对路径
- 相对路径
./座右铭.txt
当前目录下的座右铭.txt座右铭.txt
等效于上面的写法../座右铭.txt
当前目录的上一级目录中的座右铭.txt
- 绝对路径
D:/Program Files
windows 系统下的绝对路径/usr/bin
Linux 系统下的绝对路径
相对路径中所谓的 当前目录 ,指的是 命令行的工作目录 ,而并非是文件的所在目录。
__dirname,“全局变量”,始终保存所在文件的所在目录的绝对路径
3. Path模块
1. 常用API
API | 说明 |
---|---|
path.resolve | 拼接规范的绝对路径 |
path.sep | 获取操作系统的路径分隔 |
path.parse | 解析路径并返回对象 |
path.basename | 获取路径的基础名称 |
path.dirname | 获取路径的目录名 |
path.extname | 获取路径的扩展名 |
2. 使用
const fs = require('fs');
const path = require('path');fs.writeFileSync(__dirname + '/index.html', 'love');
console.log(__dirname + '/index.html');// path.resolve 拼接规范的绝对路径 ./index.html == index.html
// path.resolve(绝对路径,相对路径)
console.log(path.resolve(__dirname, './index.html')); // D:\QD\nodejs\03-_path模块\index.html// path.sep 获取操作系统的路径分隔符
console.log(path.sep); // \// path.parse 解析路径并返回对象
// __filename “全局变量”,保存文件的绝对路径
console.log(__filename); // D:\QD\nodejs\03-_path模块\path.js
let str = 'D:\\QD\\nodejs\\03-_path模块\\path.js';
console.log(path.parse(str));console.log(path.basename(str)); // path.js 获取文件名
console.log(path.dirname(str)); // D:\QD\nodejs\03-_path模块 获取文件夹名字
console.log(path.extname(str)); // 获取扩展名 .js
4. HTTP
本地回环IP地址: 127.0.0.1 ~ 127.255.255.254
1. 创建http服务
// 1. 导入 http 模块
const http = require('http');
// 2. 创建服务对象 createServer(function(请求,响应){}) 函数在接收到请求时执行
// 第一个实参:对象,请求报文的封装对象
// 第二个实参:对象,响应报文的封装
const server = http.createServer((request, response) => {// response.end('hello HTTP'); // 设置响应体 并结束响应response.setHeader('content-type', 'text/html;charset=utf-8'); // 响应内容中文代码的解决办法 response.setHeader('content-type', 'text/html;charset=utf-8')response.end('你好'); });
// 3. 监听端口,启动服务
server.listen(9000, () => { // 当服务启动成功以后执行console.log('服务已经启动'); // 当服务启动后,更新代码必须重启服务才能生效
});// Ctrl + c ,停止服务// HTTP协议默认端口是80,HTTPS协议的默认端口是443// 端口被其他程序占用,在资源监视器找到占用端口的程序 使用任务管理器关闭对应的程序
2. 获取HTTP请求报文
含义 | 语法 |
---|---|
请求方法 | request.method |
请求版本 | request.httpVersion |
请求路径 | request.url |
URL路径 | request(‘url’).parse(request.url).pathname |
URL查询字符串 | request(‘url’).parse(request.url, true).query |
请求头 | request.headers |
请求体 | request.on(‘data’, function(chunk(){})) ,request.on(‘end’, function(){}) |
- 提取HTTP报文
// 1. 导入 http 模块
const http = require('http');
// 2. 创建服务对象 createServer(function(请求,响应){}) 函数在接收到请求时执行
const server = http.createServer((request, response) => {// 获取请求的方法console.log(request.method); // GET// 获取请求的urlconsole.log(request.url); // localhost:9000 只包含路径与查询字符串// 获取HTTP协议的版本号console.log(request.httpVersion); // 1.1// 获取HTTP的请求头(返回一个对象)console.log(request.headers);console.log(request.headers.host);response.end('http'); // 设置响应体
});
// 3. 监听端口,启动服务
server.listen(9000, () => { // 当服务启动成功以后执行console.log('服务已经启动');
});
- 提取HTTP报文请求体
// 1. 导入 http 模块
const http = require('http');
// 2. 创建服务对象 createServer(function(请求,响应){}) 函数在接收到请求时执行
const server = http.createServer((request, response) => {// (1) 声明一个变量let body = '';// (2) 绑定data事件request.on('data', chunk => { // chunk 就是 Buffer,会自动转换为字符串body += chunk;});// (3) 绑定end事件request.on('end', () => { // 数据读完触发console.log(body);// 响应response.end('HELLO HTTP');});
});
// 3. 监听端口,启动服务
server.listen(9000, () => {console.log('服务已经启动');
});
- 提取HTTP报文中url路径与查询字符串(一)
// 导入 http 模块
const http = require('http');
// 1. 导入 url 模块
const url = require('url');
// 创建服务对象 createServer(function(请求,响应){}) 函数在接收到请求时执行
const server = http.createServer((request, response) => {// 2. 解析 request.urllet res = url.parse(request.url, true); // 第二个参数设为true,则res.query属性结果为对象// console.log(res);// 路径 res.pathname let pathname = res.pathname;// 查询字符串let keyword = res.query.keyword; // 属性名 => 键response.end('url');
});
// 监听端口,启动服务
server.listen(9000, () => {console.log('服务已经启动');
});
- 提取HTTP报文中url路径与查询字符串(二)
// 导入 http 模块
const http = require('http');
// 创建服务对象 createServer(function(请求,响应){}) 函数在接收到请求时执行
const server = http.createServer((request, response) => {// 实例化 url 对象// let url = new URL('http://www.xxxx.com/search?a=100&b=200');// let url = new URL('/search?a=100&b=200','http://www.xxxx.com');// console.log(url);let url = new URL(request.url, 'http://127.0.0.1:9000');// 输出路径 url.pathnameconsole.log(url.pathname);// 输出keyword查询字符串 url.searchParams.get('键名')console.log(url.searchParams.get('keyword'));response.end('url new');
});
// 监听端口,启动服务
server.listen(9000, () => {console.log('服务已经启动');
});
3. 设置HTTP响应报文
作用 | 语法 |
---|---|
设置响应状态码 | response.statusCode |
设置响应状态描述 | response.statusMessage |
设置响应头信息 | response.setHeader(‘头名’,‘头值’) |
设置响应体 | response.write(‘xx’), response.end(‘xx’) |
解决中文乱码问题 : response.setHeader("Content-Type","text/html;charset=utf-8")
1
- 设置响应报文练习
// 导入 http 模块
const http = require('http');
// 创建服务对象
const server = http.createServer((request, response) => { // 有 而且 只能有一个 response.end()// 1. 设置响应状态码(默认200) response.statusCode = response.statusCode = 203;// 2. 设置响应状态描述 response.statusMessage (用的特别少)response.statusMessage = 'abcd';// 3. 设置响应头信息 response.setHeader('头名', '头值') response.setHeader('content-type','text/html;charset=utf-8')response.setHeader('myHeader','test test test');response.setHeader('test', ['a', 'b', 'c']); // 设置多个同名的响应头// 4. 设置响应体 response.write('xx') response.end('xxx');response.write('love'); // 如果使用了write一般就不再用end , write()可以多次调用response.write('love');response.write('love');response.write('love');response.end('HTTP');
});
// 监听端口,启动服务
server.listen(9000, () => {console.log('服务器已启动,端口9000监听中');
});
- HTTP响应练习:响应一个html文件内容
const http = require('http');
const fs = require('fs');
const server = http.createServer((request, response) => {let html = fs.readFileSync('./09表格.html'); // 读取文件内容response.end(html); // end 参数可以是字符串或Buffer
});
server.listen(9000, () => {console.log('服务已经启动,端口9000正在监听中');
});
4. MIME类型 设置响应体utf-8的优先级高于标签
- 媒体类型(通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。
HTTP 服务可以设置响应头 Content-Type 来表明响应体的 MIME 类型,浏览器会根据该类型决定如何处理
资源。 - 常见文件对应的 mime 类型
application/json
html: 'text/html',
css: 'text/css',
js: 'text/javascript',
png: 'image/png',
jpg: 'image/jpeg',
gif: 'image/gif',
mp4: 'video/mp4',
mp3: 'audio/mpeg',
json: 'application/json'
对于未知的资源类型,可以选择 application/octet-stream
类型,浏览器在遇到该类型的响应时,会对响应体内容进行独立存储,也就是我们常见的 下载 效果。 response.setHeader('content-type', 'application/octet-stream;charset=utf-8');
5. GET、POST请求的区别
GET 和 POST 是 HTTP 协议请求的两种方式:
- GET 主要用来获取数据,POST 主要用来提交数据
- GET 带参数请求是将参数缀到 URL 之后,在地址栏中输入 url 访问网站就是 GET 请求,
POST 带参数请求是将参数放到请求体中 - POST 请求相对 GET 安全一些,因为在浏览器中参数会暴露在地址栏
- GET 请求大小有限制,一般为 2K,而 POST 请求则没有大小限制
5. 模块化
1. 介绍
- 什么是模块化与模块?
将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为模块化
。
其中拆分出的每个文件就是一个模块
,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用。 - 模块化好处
- 防止命名冲突
- 高复用性
- 高维护性
2. 模块化的使用
- 初体验
1. 创建 me.js
//声明函数
function tiemo(){console.log('贴膜....');
}
//暴露数据
module.exports = tiemo;
2. 创建 index.js
//导入模块
const tiemo = require('./me.js');
//调用函数
tiemo()
- 暴露数据
module.exports = value
可以暴露任意数据。exports.name = value
不能使用exports = value的形式暴露数据,模块内部module与exports的隐式关系`exports = module.exports = {}, require返回的是目标模块中module.exports的值。
示例:
// 声明一个函数
function tiemo() {console.log('贴膜');
}
function chifan() {console.log('吃饭');
}// 暴露数据
// module.exports = {
// tiemo,
// chifan
// }// exports 暴露数据
// exports.tiemo = tiemo;
// exports.chifan = chifan;// 1. module.exports 可以暴露 任意 数据
// module.exports = 'iloveyou';
// module.exports = '522';// 2. 不能使用 exports = value 的形式暴露数据,模块内部 module 与 exports 的隐式关系
// exports = 'i'; 是错误的
// exports = module.exports = {} ,require 返回的是目标模块中 module.exports 的值
// console.log(module.exports); // {}
// console.log(module.exports == exports); // true
3. 导入模块
在模块中使用 require 传入文件路径即可引入文件。
const test = require('./me.js');
require 使用的一些注意事项:
- 对于自己创建的模块,导入时路径建议写 相对路径 ,且不能省略 ./ 和 …/
js
和json
文件导入时可以不用写后缀,c/c++编写的 node 扩展文件也可以不写后缀,但是一般用不到。如果js和json文件同名,并且省略后缀导入,则先导入js文件。- 如果导入其他类型的文件,会以
js
文件进行处理 - 如果导入的路径是个文件夹,则会 首先 检测该文件夹下
package.json
文件中main
属性对应的文件,
如果存在则导入,反之如果文件不存在会报错。
如果main
属性不存在,或者package.json
不存在,则会尝试导入文件夹下的index.js
和index.json
,如果还是没找到,就会报错 - 导入 node.js 内置模块时,直接 require 模块的名字即可,无需加 ./ 和 …/
6. 命令行工具
npm
- 初始化: npm init
- 搜索包: npm s/search 关键字
- 下载安装包:npm install 包名、npm i 包名(运行之后多两个资源:node_modules 文件夹存放下载时的包、package-lock.json 包的锁文件 用来锁定包的版本,创建一个包名为A,A中安装了包B,则B是A的一个依赖包,A依赖B)
- require导入npm包的基本流程:(1)在当前文件夹下的node_modules中寻找同名的文件夹(2)在上级目录下的node_modules中寻找同名的文件夹、直到找到磁盘根目录
- 生产依赖:npm i -S uniq、npm i --save uniq(生产依赖是开发阶段和最终上线阶段运行阶段都用到的依赖包)
- 开发依赖:npm i -D less、npm i --save-dev less(开发依赖只在开发阶段使用的依赖包)
- 全局安装:选项
-g
,npm i -g nodemon - 安装项目依赖:npm i、npm install
- 安装指定版本的包:npm i 包名@版本号(npm i jquery@1.11.2)
- 删除依赖:npm remove uniq、npm r uniq (局部删除);npm remove -g nodemon (全局删除)
- 配置命令别名:配置
package.json
中的scripts
属性
{"scripts": {"server": "node server.js","start": "node index.js"}
}
如上配置完之后,可以使用npm run server
、npm run start
(npm start
)
2. cnpm
和npm
切换使用
npm
切换为cnpm
npm config set registry https://registry.npm.taobao.org
查看是否切换成功:npm config get registry
,成功则显示https://registry.npm.taobao.org/
cnpm
切换为npm
npm config set registry https://registry.npmjs.org
查看是否切换成功:npm config get registry
,成功则显示https://registry.npmjs.org/
7. express
1. 使用express
// 1. 导入 express
const express = require('express');// 2. 创建应用对象
const app = express();// 3. 创建路由
app.get('/home', (request, response) => {response.end('express,hello');
});// 4. 监听端口,启动服务
app.listen(3000, () => {console.log('服务已经启动,端口3000正在监听中...');
});
2. express 路由
- 什么是路由
路由确定了应用程序如何响应客户端对特定端点的请求。
- 路由的使用
- 一个路由的组成:
请求方法、路径、回调函数
- 使用格式:
app.方法(path, callback)
- 代码示例:
// 导入 express
const express = require('express');
// 创建应用对象
const app = express();
// 创建路由
app.get('/home', (request, response) => {response.end('hello,Express');
});
app.get('/', (request, response) => {response.setHeader('content-type', 'text/html;charset=utf-8');response.end('首页');
});
app.post('/login', (request, response) => {response.setHeader('content-type', 'text/html;charset=utf-8');response.end('登录');
});
app.all('/test', (request, response) => { // 匹配所有请求方法,get postresponse.end('test test');
});
app.all('*', (request, response) => { // 自定义 404 路由response.end('<h1>404 Not Found</h1>');
});
// 监听端口,启动服务
app.listen(3000, () => {console.log('服务已经启动,端口 3000 正在监听中');
});
- 获取请求参数
//导入 express
const express = require('express');
//创建应用对象
const app = express();
//获取请求的路由规则
app.get('/request', (req, res) => {
//1. 获取报文的方式与原生 HTTP 获取方式是兼容的
console.log(req.method);
console.log(req.url);
console.log(req.httpVersion);
console.log(req.headers);//2. express 独有的获取报文的方式
console.log(req.path); // 获取路径
console.log(req.query); // 获取查询字符串
console.log(req.ip); // 获取客户端的IP地址
console.log(req.get('host')); // 获取指定的请求头res.send('请求报文的获取');
});
//启动服务
app.listen(3000, () => {
console.log('启动成功....')
})
- 获取路由参数
req.params.id
=> 获取路由参数id
路由参数指的是 URL 路径中的参数(数据)。
app.get('/:id.html', (req, res) => {
res.send('商品详情, 商品 id 为' + req.params.id);
});
3. express响应设置
- express框架封装了一些API方便响应数据,并且兼容原生HTTP模块的获取方式
express中res.send()设置响应体,不会出现乱码
const express = require('express');const app = express();app.get('/response', (req, res) => {// 1. 原生响应设置res.statusCode = 404; // 设置响应状态码res.statusMessage = '123'; // 设置响应状态描述res.setHeader('abc', 'xyz'); // 设置响应头res.write('hello Express'); // 设置响应体res.end('response'); // 设置响应体// 2. express 响应方法// res.status(500); // 设置响应状态码// res.set('aaa', 'bbb'); // 设置响应头// res.send('你好 express'); // 设置响应体res.status(500).set('aaa', 'ddd').send('你好Express');
});app.listen(3000, () => {console.log('服务已经启动,端口 3000 正在监听中');
});
- 其他响应设置
const express = require('express');const app = express();app.get('/other', (req, res) => {res.redirect('https://www.baidu.com'); // 重定向 跳转响应res.download(__dirname + '/package.json'); // 下载响应,传入文件的绝对路径res.json({name: 'yuzuru',slogon: '努力'}); // json响应res.sendFile(__dirname + '/test.html'); // 响应文件test.html中的内容
});app.listen(3000, () => {console.log('服务已经启动,端口 3000 正在监听中');
});
4. 中间件
- 什么是中间件
中间件(Middleware)本质是一个回调函数
。
中间件函数 可以像路由回调一样访问 请求对象(request) , 响应对象(response) - 中间件的作用
中间件的作用 就是使用函数封装公共操作,简化代码
- 中间件的类型
- 全局中间件
- 路由中间件
- 使用中间件
- 全局中间件
每一个请求 到达服务端之后 都会执行全局中间件函数。
// 需求:记录每个请求的 url 与 IP 地址
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
// 声明中间件函数
function recordMiddleware(req, res, next) {let {url, ip} = req; // 获取 url ipfs.appendFileSync(path.resolve(__dirname, './access.log'), `${url} ${ip}\r\n`); // 将信息保存在文件 access.lognext(); // 调用next
}
// 使用中间件函数
app.use(recordMiddleware);
app.get('/home', (req, res) => {res.send('前台首页');
});
app.get('/admin', (req, res) => {res.send('后台首页');
});
app.all('*', (req, res) => {res.send('<h1>404 Not Found</h1>');
});
app.listen(3000, () => {console.log('服务已经启动,端口 3000 正在监听中');
});
- 路由中间件
如果 只需要对某一些路由进行功能封装 ,则就需要路由中间件
调用格式如下:
app.get('/路径',
中间件函数, (request, response) => {});
app.get('/路径',
中间件函数1,
中间件函数2, (request, response) => {});
使用案例:
// 需求:/admin /setting 的请求,要求 URL 携带 code=521 参数,如未携带 提示 【暗号错误】
const express = require('express');
const app = express();
function checkCodeMiddleware(req, res, next) {if (req.query.code === '521') { // 判断URL code是否等于521next();} else {res.send('【暗号错误】');}
}
app.get('/setting', checkCodeMiddleware, (req, res) => {res.send('设置页面');
});
app.get('/admin', checkCodeMiddleware, (req, res) => {res.send('后台首页');
});
app.all('*', (req, res) => {res.send('<h1>404 Not Found</h1>');
});
app.listen(3000, () => {console.log('服务已经启动,端口 3000 正在监听中');
});
- 静态资源中间件
- 使用案例:
//引入express框架
const express = require('express');
//创建服务对象
const app = express();app.use(express.static(__dirname + '/public')); // 静态资源中间件设置
//如果访问的内容经常变化,还是需要设置路由
//但是,在这里有一个问题,如果public目录下有index.html文件,单独也有index.html的路由,则谁书写在前,优先执行谁app.get('/index.html', (request, response) => {response.send('首页');
});
//监听端口
app.listen(3000, () => {console.log('3000 端口启动....');
});
- 注意事项:
- index.html 文件为默认打开的资源
- 如果静态资源与路由规则同时匹配,谁先匹配谁就响应
- 路由响应动态资源,静态资源中间件响应静态资源
5. express中获取请求体数据-body-parser
- 第一步:安装
npm i body-parser
- 第二步:导入 body-parser 包
const bodyParser = require('body-parser');
- 第三步:获取中间件函数
//处理 querystring 格式的请求体
let urlParser = bodyParser.urlencoded({extended:false});
//处理 JSON 格式的请求体
let jsonParser = bodyParser.json();
- 第四步:设置路由中间件
urlParser
,然后使用 request.body 来获取请求体数据
app.post('/login', urlParser, (request,response)=>{//console.log(request.body); //获取请求体数据console.log(request.body.username); //用户名console.log(request.body.userpass); //密码response.send('获取请求体数据');
});
示例:
// 需求: GET /login 显示表单网页
// POST /login 获取表单中的 用户名 和 密码
const express = require('express');
const bodyParser = require('body-parser');
const app = express();// 解析json格式的请求体的中间件
const jsonParser = bodyParser.json();
// 解析 querystring 格式请求体的中间件
var urlencodedParser = bodyParser.urlencoded({ extended: false })app.get('/login', (req, res) => {// res.send('表单页面');// 响应 HTML 文件内容res.sendFile(__dirname + '/11_form.html');
});
app.post('/login', urlencodedParser, (req, res) => {// 获取用户名和密码 中间件函数执行完毕以后,会往请求对象身上添加body属性console.log(req.body);res.send('获取用户的数据');
});app.listen(3000, () => {console.log('服务已经启动,端口 3000 正在监听中');
});
6. express防盗链的使用
const express = require('express');const app = express();// 声明中间件
app.use((req, res, next) => {// 检测请求头中的 referer 是否为 127.0.0.1let referer = req.get('referer');if (referer) {// 实例化let url = new URL(referer);console.log(url);// 获取hostnamelet hostname = url.hostname;console.log(hostname); // 127.0.0.1 localhost// 判断if (hostname != '127.0.0.1') {// 响应404res.status(404).send('<h1>404 Not Found</h1>');return;}}next();
});// 静态资源中间件设置
app.use(express.static(__dirname + '/public'));app.listen(3000, () => {console.log('服务已经启动,端口 3000 正在监听中');
});
7. 路由模块化Router express.Rounter()
- 子文件
homeRouter.js
// 1. 导入express
const express = require('express')
// 2. 创建路由器对象
const router = express.Router();
// 3. 在router对象身上添加路由
router.get('/setting', (req, res)=>{res.send('设置页面');
})
router.get('/admin', (req, res)=>{res.send('后台首页');
})
// 4. 暴露router对象
module.exports = router;
- 主文件
const express = require('express');
const app = express();// 5. 引入子路由文件
const homeRouter = require('./routes/homeRouter.js');
// 6. 设置和使用中间件
app.use(homeRouter);app.all('*', (req, res)=>{res.send('<h1>404 NOT FOUND</h1>');
})
app.listen(3000, ()=>{console.log('服务已经启动');
})
8. 模板引擎
- 什么是模板引擎
模板引擎
是分离用户界面和业务数据
的一种技术。 EJS
模板引擎
- 下载安装EJS:
npm i ejs --save
- 使用EJS
(1)列表渲染
// js文件 ,实现列表渲染
const ejs = require('ejs');
const fs = require('fs');
const xiyou = ['唐僧', '孙悟空', '猪八戒', '沙僧'];
/* <ul><% xiyou.forEach(item => { %><li><%= item %></li><% }) %>
</ul> */
// <% %> 可以对 <% %>包含的js代码执行,forEach可以进行循环处理// EJS 实现
let str = fs.readFileSync('./02_xiyou.html').toString();
let result = ejs.render(str, {xiyou: xiyou});
console.log(result);
// html文件
<!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><h2>西游</h2><ul><% xiyou.forEach(item => { %><li><%= item %></li><% }) %></ul>
</body>
</html>
(2)条件渲染
// js 文件
const ejs = require('ejs');
const isLogin = true;
const result = ejs.render(`<% if(isLogin){ %><span>欢迎回来</span><% }else {%><button>登录</button><% } %>`, { isLogin: isLogin }
)
console.log(result);
- EJS语法
<% %>
: JS代码<%= %>
: 转义的数据(<%= item %>
)<%- %>
: 非转义的数据
相关文章:

nodeJs基础笔记
title: nodeJs基础笔记 date: 2023-11-18 22:33:54 tags: 1. Buffer 1. 概念 Buffer 是一个类似于数组的 对象 ,用于表示固定长度的字节序列。 Buffer 本质是一段内存空间,专门用来处理 二进制数据 。 2. 特点 Buffer 大小固定且无法调整Buffer 性能…...

Skywalking流程分析_9(JDK类库中增强流程)
前言 之前的文章详细介绍了关于非JDK类库的静态方法、构造方法、实例方法的增强拦截流程,本文会详细分析JDK类库中的类是如何被增强拦截的 回到最开始的SkyWalkingAgent#premain try {/** 里面有个重点逻辑 把一些类注入到Boostrap类加载器中 为了解决Bootstrap类…...

矩阵的QR分解
矩阵的QR分解 GramSchmidt 设存在 B { x 1 , x 2 , … , x n } \mathcal{B}\left\{\mathbf{x}_{1},\mathbf{x}_{2},\ldots,\mathbf{x}_{n}\right\} B{x1,x2,…,xn}在施密特正交化过程中 q 1 x 1 ∣ ∣ x 1 ∣ ∣ q_1\frac{x_1}{||x_1||} q1∣∣x1∣∣x1 q k …...

STL总结
STL vector 头文件<vector> 初始化,定义,定义长度,定义长度并且赋值,从数组中获取数据返回元素个数size()判断是否为空empty()返回第一个元素front()返回最后一个数back()删除最后一个数pop_back()插入push_back(x)清空clear()begin()end()使用s…...

资深测试总结,现在软件测试有未来吗?“你“的底气在哪里?
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、为什么会有 “…...

Scalable Exact Inference in Multi-Output Gaussian Processes
Orthogonal Instantaneous Linear Mixing Model TY are m-dimensional summaries,ILMM means ‘Instantaneous Linear Mixing Model’,OILMM means ‘Orthogonal Instantaneous Linear Mixing Model’ 辅助信息 作者未提供代码...

sqli-labs(Less-3)
1. 通过构造id1’ 和id1’) 和id1’)–确定存在注入 可知原始url为 id(‘1’) 2.使用order by 语句猜字段数 http://127.0.0.1/sqlilabs/Less-3/?id1) order by 4 -- http://127.0.0.1/sqlilabs/Less-3/?id1) order by 3 --3. 使用联合查询union select http://127.0.0.1…...

集合框架面试题
一、集合容器的概述 1. 什么是集合 集合框架:用于存储数据的容器。 集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。 任何集合框架都包含三大块内容: 对外的接口、接口的实现和对集合运算的算 法。 接口:表示集合的抽象数据…...

【LeetCode刷题日志】225.用队列实现栈
🎈个人主页:库库的里昂 🎐C/C领域新星创作者 🎉欢迎 👍点赞✍评论⭐收藏✨收录专栏:LeetCode 刷题日志🤝希望作者的文章能对你有所帮助,有不足的地方请在评论区留言指正,…...

【JavaScript】fetch 处理流式数据,实现类 chatgpt 对话
本文只包含最基础的请求后端大佬给得对话接口,大部分模型的传参是差不多的,核心还是如何处理 fetch 获取的流数据 import { defineStore } from pinia; import { ElMessage } from element-plus;type Role system | user | assistant; export interfac…...

收发电子邮件
电子邮件是Internet提供的又一个重要服务项目。早在1987年9月20日,中国首封电子邮件就是从北京经意大利向前联邦德国卡尔斯鲁厄大学发出的,在中国首次实现了与Internet的连接,使中国成为国际互联网大家庭中的一员。现在随着Internet的迅速发展…...

sql13(Leetcode570至少有5名直接下属的经理)
代码: 脑子记不住 语法全靠试.. # Write your MySQL query statement below select b.name from (select managerId,count(managerId) as numfrom Employeegroup by managerId ) a left join Employee b on a.managerIdb.id where a.num>5 and b.name is not N…...

15分钟,不,用模板做数据可视化只需5分钟
测试显示,一个对奥威BI软件不太熟悉的人来开发数据可视化报表,要15分钟,而当这个人去套用数据可视化模板做报表,只需5分钟! 数据可视化模板是奥威BI上的一个特色功能板块。用户下载后更新数据源,立即就能获…...

C 语言字符串函数
C 语言字符串函数 在本文中,您将学习使用诸如gets(),puts,strlen()等库函数在C中操作字符串。您将学习从用户那里获取字符串并对该字符串执行操作。 您通常需要根据问题的需要来操作字符串。大多数字符串操作都可以自定义方法完成ÿ…...

nvm安装详细教程(卸载旧的nodejs,安装nvm、node、npm、cnpm、yarn及环境变量配置)
文章目录 一、完全卸载旧的nodejs1、打开系统的控制面板,点击卸载程序,卸载nodejs(1)打开系统的控制面板,点击程序下的卸载程序(2)找到node.js,鼠标右击出现下拉框,点卸载…...

详细步骤记录:持续集成Jenkins自动化部署一个Maven项目
Jenkins自动化部署 提示:本教程基于CentOS Linux 7系统下进行 Jenkins的安装 1. 下载安装jdk11 官网下载地址:https://www.oracle.com/cn/java/technologies/javase/jdk11-archive-downloads.html 本文档教程选择的是jdk-11.0.20_linux-x64_bin.tar.g…...

Python学习(一)基础语法
文章目录 1. 入门1.1 解释器的作用1.2 下载1.3 基础语法输入输出语法与引号注释:变量: 数据类型与四则运算数据类型四则运算数据类型的查看type()数据类型的转换int()、int()、float() 流程控制格式化输出循环与遍历逻辑运算符list遍历字典dict遍历 跳出…...

【C刷题】day7
🎥 个人主页:深鱼~🔥收录专栏:【C】每日一练🌄欢迎 👍点赞✍评论⭐收藏 一、选择题 1、以下对C语言函数的有关描述中,正确的有【多选】( ) A: 在C语言中,一…...

数据挖掘复盘——apriori
read_csv函数返回的数据类型是Dataframe类型 对于Dataframe类型使用条件表达式 dfdf.loc[df.loc[:,0]2]df: 这是一个DataFrame对象的变量名,表示一个二维的表格型数据结构,类似于电子表格或SQL表。 df.loc[:, 0]: 这是使用DataFrame的.loc属性来进行…...

Windows10下Maven3.9.5安装教程
文章目录 1.下载maven2.安装3.配置系统变量3.1.新建系统变量 MAVEN_HOME3.2.编辑系统变量Path 4.CMD命令测试是否安装成功5.配置maven本地仓库6.配置国内镜像仓库 1.下载maven 官网 https://maven.apache.org/download.cgi 点击下载。 2.安装 解压到指定目录 D:\installSoft…...

【开源】基于JAVA的校园失物招领管理系统
项目编号: S 006 ,文末获取源码。 \color{red}{项目编号:S006,文末获取源码。} 项目编号:S006,文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 招领管理模块2.2 寻物管理模块2.3 系…...

requests爬虫IP连接初始化问题及解决方案
问题背景 在使用HTTPS爬虫IP连接时,如果第一次请求是chunked方式,那么HTTPS爬虫IP连接将不会被初始化。这个问题可能会导致403错误,或者在使用HTTPS爬虫IP时出现SSL错误。 解决方案 为了解决这个问题,我们可以在requests库的ada…...

Argo Rollouts结合Service进行Blue-Green部署
删除03 部署04 rootk8s-master01:~/learning-jenkins-cicd/09-argocd-and-rollout/rollout-demos# kubectl delete -f 03-rollouts-with-prometheus-analysis.yaml rootk8s-master01:~/learning-jenkins-cicd/09-argocd-and-rollout/rollout-demos# kubectl apply -f 04-rol…...

mongodb——原理简介,docker单机部署
MongoDB noSQL数据库 特点 数据文件存储格式为 BSON (JSON 的扩展) {“name”:“joe”}这是 BSON 的例子,其中"name"是键,"joe"是值。键值对组成了 BSON 格式。面向集合…...

ThinkPHP 系列漏洞
目录 2、thinkphp5 sql注入2 3、thinkphp5 sql注入3 4、 thinkphp5 SQL注入4 5、 thinkphp5 sql注入5 6、 thinkphp5 sql注入6 7、thinkphp5 文件包含漏洞 8、ThinkPHP5 RCE 1 9、ThinkPHP5 RCE 2 10、ThinkPHP5 rce3 11、ThinkPHP 5.0.X 反序列化漏洞 12、ThinkPHP…...

系列十、你说你做过JVM调优和参数配置,请问如何盘点JVM系统的默认值?
一、JVM的参数类型 1.1、标配参数 java -versionjava -help 1.2、XX参数 1.2.1、Boolean类型 公式:-XX:或者- 某个属性值 表示开启、-表示关闭 # 是否打印GC收集细节 -XX:PrintGCDetails -XX:-PrintGCDetails# 是否使用串行垃圾收集器 -XX:UseSerialGC -XX:-UseS…...

Java Web——Web开发介绍
什么是Web开发 Web开发是一种创建和维护全球广域网(World Wide Web)上的网站和应用的技术。全球广域网也称为万维网(www World Wide Web),是一个能够通过浏览器访问的互联网上的巨大信息库。 Web开发的目标是创建功能齐全、易于使用和安全的…...

Vue 数据监听机制及 Vue 2.0 和 Vue 3.0 的比较
Vue 数据监听机制 在 Vue 中,数据的变化通常是通过数据劫持(Data Binding)和观察者模式来实现的。当数据发生变化时,Vue 能够自动更新视图。 Vue 2.0 的数据监听 在 Vue 2.0 中,数据监听是通过 Object.defineProper…...

QT多线程项目中子线程无法修改主线程的ui组件
情况描述 今天我创建了一个QT多线程的工程,框架如下。我希望通过指针的方式,让子线程去直接修改主线程的ui组件,但事与愿违。 class ChildThread : public QThread {Q_OBJECT public:ChildThread (MainThread* par):m_Par(par){}; protecte…...

Python 如何实现备忘录设计模式?什么是备忘录设计模式?Python 备忘录设计模式示例代码
什么是备忘录(Memento)设计模式? 备忘录(Memento)设计模式是一种行为型设计模式,用于捕获一个对象的内部状态,并在对象之外保存这个状态,以便在需要时恢复对象到先前的状态。这种模…...