nodeJs基础笔记


title: nodeJs基础笔记
date: 2023-11-18 22:33:54
tags:

1. Buffer

1. 概念

Buffer 是一个类似于数组的 对象 ,用于表示固定长度的字节序列。
Buffer 本质是一段内存空间,专门用来处理 二进制数据 。

2. 特点
  1. Buffer 大小固定且无法调整
  2. Buffer 性能较好,可以直接对计算机内存进行操作
  3. 每个元素的大小为 1 字节(byte)
3. 使用
  1. 创建 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]);
  1. Buffer 与字符串的转化
    我们可以借助 toString 方法将 Buffer 转为字符串。toString 默认是按照 utf-8 编码方式进行转换的。
let buf_4 = Buffer.from([105, 108, 111, 118, 101, 121, 111, 117]);
console.log(buf_4.toString())
  1. 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. 文件写入 (当需要持久化保存数据的时候,应该想到文件写入)
  1. 同步写入
    fs.writeFileSync(文件名,待写入的数据[,选项设置 可选])
  2. 异步写入
    fs.writeFile(文件名,待写入的数据[,选项设置 可选],回调函数) 该函数的第三个参数设置为{flag: 'a'}可以实现追加写入
  • 如果文件不存在,会自动创建文件
  • 当写入完成,会自动调用执行函数
  • 以下代码中,当写入成功 err: null ;当写入失败 err的值是一个错误对象
const fs = require('fs');
fs.writeFile('./座右铭.txt', '三人行,则必有我师', err=>{
  if (err) {
    console.log('写入失败');
    return;
  }
  console.log('写入成功', err); // 写入成功 null
})
  1. 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('追加成功');
});
  1. 流式写入
  • 语法: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. 文件读取
  1. 异步读取
    readFile(文件[,option],回调函数(a, b)) ,回调函数两个参数。
  2. 同步读取
    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());
  1. 流式读取
    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('读取完成');
});
  1. 练习-复制文件
// 需求: 复制 笑看风云.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. 文件的重命名与移动
  1. 语法
    fs.rename(oldPath, newPath, callback)
    fs.renameSync(oldPath, newPath)
  2. 示例
// 重命名、文件移动,都是改变了 文件的路径
// (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. 文件的删除
  1. fs.unlink(path, callback)fs.unlinkSync(path) 同步
  2. 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. 文件夹操作
  1. 创建文件夹
    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('创建成功');
});
  1. 读取文件夹
    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);
});
  1. 删除文件夹
    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. 相对路径和绝对路径
  1. 相对路径
  • ./座右铭.txt 当前目录下的座右铭.txt
  • 座右铭.txt 等效于上面的写法
  • ../座右铭.txt 当前目录的上一级目录中的座右铭.txt
  1. 绝对路径
  • 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的优先级高于标签
  1. 媒体类型(通常称为 Multipurpose Internet Mail Extensions 或 MIME 类型 )是一种标准,用来表示文档、文件或字节流的性质和格式。
    HTTP 服务可以设置响应头 Content-Type 来表明响应体的 MIME 类型,浏览器会根据该类型决定如何处理
    资源。
  2. 常见文件对应的 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 协议请求的两种方式:

  1. GET 主要用来获取数据,POST 主要用来提交数据
  2. GET 带参数请求是将参数缀到 URL 之后,在地址栏中输入 url 访问网站就是 GET 请求,
    POST 带参数请求是将参数放到请求体中
  3. POST 请求相对 GET 安全一些,因为在浏览器中参数会暴露在地址栏
  4. GET 请求大小有限制,一般为 2K,而 POST 请求则没有大小限制

5. 模块化

1. 介绍
  1. 什么是模块化与模块?
    将一个复杂的程序文件依据一定规则(规范)拆分成多个文件的过程称之为 模块化
    其中拆分出的 每个文件就是一个模块 ,模块的内部数据是私有的,不过模块可以暴露内部数据以便其他模块使用。
  2. 模块化好处
  • 防止命名冲突
  • 高复用性
  • 高维护性
2. 模块化的使用
  1. 初体验
1. 创建 me.js
//声明函数
function tiemo(){
  console.log('贴膜....');
}
//暴露数据
module.exports = tiemo;
2. 创建 index.js
//导入模块
const tiemo = require('./me.js');
//调用函数
tiemo()
  1. 暴露数据
  • 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 使用的一些注意事项:

  1. 对于自己创建的模块,导入时路径建议写 相对路径 ,且不能省略 ./ 和 …/
  2. jsjson 文件导入时可以不用写后缀,c/c++编写的 node 扩展文件也可以不写后缀,但是一般用不到。如果js和json文件同名,并且省略后缀导入,则先导入js文件。
  3. 如果导入其他类型的文件,会以 js 文件进行处理
  4. 如果导入的路径是个文件夹,则会 首先 检测该文件夹下 package.json 文件中 main 属性对应的文件,
    如果存在则导入,反之如果文件不存在会报错。
    如果 main 属性不存在,或者 package.json 不存在,则会尝试导入文件夹下的 index.jsindex.json ,如果还是没找到,就会报错
  5. 导入 node.js 内置模块时,直接 require 模块的名字即可,无需加 ./ 和 …/

6. 命令行工具

  1. 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 servernpm run start(npm start)
2. cnpmnpm切换使用

  • 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 路由
  1. 什么是路由
    路由确定了应用程序如何响应客户端对特定端点的请求。
  2. 路由的使用
  • 一个路由的组成:请求方法、路径、回调函数
  • 使用格式: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 正在监听中');
});
  1. 获取请求参数
//导入 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('启动成功....')
})
  1. 获取路由参数 req.params.id => 获取路由参数id
    路由参数指的是 URL 路径中的参数(数据)。
app.get('/:id.html', (req, res) => {
res.send('商品详情, 商品 id 为' + req.params.id);
});
3. express响应设置
  1. 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 正在监听中');
});
  1. 其他响应设置
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. 中间件
  1. 什么是中间件
    中间件(Middleware)本质是一个回调函数
    中间件函数 可以像路由回调一样访问 请求对象(request) , 响应对象(response)
  2. 中间件的作用
    中间件的作用 就是 使用函数封装公共操作,简化代码
  3. 中间件的类型
  • 全局中间件
  • 路由中间件
  1. 使用中间件
  • 全局中间件
    每一个请求 到达服务端之后 都会执行全局中间件函数。
// 需求:记录每个请求的 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 正在监听中');
});
  1. 静态资源中间件
  • 使用案例:
//引入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
  1. 第一步:安装
    npm i body-parser
  2. 第二步:导入 body-parser 包
    const bodyParser = require('body-parser');
  3. 第三步:获取中间件函数
//处理 querystring 格式的请求体
let urlParser = bodyParser.urlencoded({extended:false});
//处理 JSON 格式的请求体
let jsonParser = bodyParser.json();
  1. 第四步:设置路由中间件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()
  1. 子文件 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;
  1. 主文件
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. 模板引擎
  1. 什么是模板引擎
    模板引擎分离用户界面和业务数据 的一种技术。
  2. 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 %>
    • <%- %>: 非转义的数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值