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
// 获取请求的url
console.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.url
let 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.pathname
console.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 post
response.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 ip
fs.appendFileSync(path.resolve(__dirname, './access.log'), `${url} ${ip}\r\n`); // 将信息保存在文件 access.log
next(); // 调用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是否等于521
next();
} 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.1
let referer = req.get('referer');
if (referer) {
// 实例化
let url = new URL(referer);
console.log(url);
// 获取hostname
let hostname = url.hostname;
console.log(hostname); // 127.0.0.1 localhost
// 判断
if (hostname != '127.0.0.1') {
// 响应404
res.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 %>
)<%- %>
: 非转义的数据