node js md 版

数据类型

buffer

1.let buf = Buffer.alloc(10) //<Buffer 00 00 00 00 00 00 00 00 00 00>
	1.安全 但速度慢
2.let buf = Buffer.allocUnsafe(10) //<Buffer 88 62 9c 04 00 00 00 00 f8 61>
	1.不安全 可能会引用到别的程序的数据 但是速度块
3.let buf = Buffer.from("abc");// <Buffer 61 62 63> //这里都是 16 进制
4.let buf = Buffer.from([65,66,67]); // <Buffer 41 42 43> //这里都是 16 进制
	1.buf.toString() //ABC
5.可以通过 buf[0] buf[1] 访问
	1.buf[0] 最大表示 8 个二进制位 11111111 也就是 255 所以超过 255 就会把最高位的全部舍弃
	2.buf[0] = 361 // <Buffer 69 42 43>
		1.361 十六进制 169 [0001 0110 1001] 0001被舍弃 变成 [0110 1001] 对应的 16 进制 69

模块引入的注意事项

1.const mod = require("./module") module 模块中暴露 exports = module.exports = {function test(){},name:"zs"}
	1.当修改 exports = {name:"ls"} 无用 因为最后模块导出的 只认 module.exports 所以 exports.say = function(){} 这个可以
2.关于引入模块 require("./modeul") 省略扩展名的问题
	1.先寻找modulel.js 再寻找 module.json 有就导入 停止寻找 如果最后找不到 报错
	2.对于其他类型的扩展名 都按 .js 省略来看 比如 module.abc 看作 module.abc.js
	3.如果导入的是个路径 先检查文件夹下 package.json 里面的 main属性 对应的文件 并查找这个文件  如果没有这个属性
		1.或者 package.json 不存在 就找文件夹下的 index.js index.json 如果还没有找到就报错
	4.导入内置模块 不用加 ./

npm的基本使用细则[初始化等]

1.npm init -y 一切按默认创建 npm init 一步一个提醒 最后生成一个 package.json
	1.包名字不能有中文和大写 [默认是文件夹名字 所以文件夹也不能取这样名字]
2.常用命令
	1.npm i uniq [-S --save 默认参数] 安装后 信息保存再 package.json dependencies 中
		1.生产依赖
		2.安装指定版本包 npm i jquery@1.11.2
	2.npm i math -D/--save-dev 安装后 保存在 package.json devDependencies 中
		2.开发依赖
	>3.npm i nodemon -g 全局包 [nodemon t.js 修改js后 自己重新运行 对于 server 类的服务器文件 很有用]
	4.npm root -g 可以查看 全局包 的路径
		1.关于 npm root 子命令
			1. npm root -g
				1.C:\Program Files\nodejs\node_modules
	5.npm i/install 什么参数都不写 会安装所有的包依赖 根据  package.json 和 package-lock.json
	6.npm r/remove uinq 不带参数 删除本项目的包 [development 或 production]
		1.如果要删除全局的包 npm r nodemon -g
	7.npm config list 查看当前 npm 的 一些配置 包括 下载源之类
		1.关于 npm config 子命令
			1.npm config get registry
			2.npm config set registry https://registry.npmmirror.com/
			3.npm config list
				home = "https://npmmirror.com" 
				registry = "https://registry.npmmirror.com/" 
				; node bin location = C:\Program Files\nodejs\node.exe
				; node version = v16.20.1
				; npm local prefix = S:\project 2021\other\ChromeHelper\chrome tomorrow\study\nodeTest\baseTest
				; npm version = 8.19.4
				; cwd = S:\project 2021\other\ChromeHelper\chrome tomorrow\study\nodeTest\baseTest\git-test\hello
				; HOME = C:\Users\Administrator
				; Run `npm config ls -l` to show all defaults
			4.npm config edit
				1.直接打开文件编辑 比如 镜像源 之类的
			5.npm get/set userconfig 获取/设置 edit 所编辑文件的路径
				1.C:\Users\Administrator\.npmrc
3.关于 npm 向上级查找的特性
	1.运行包后 首先在 本目录的 node_modules 中查找需要的包 没有的化 向上级目录 node_module 直到 根目录
	2.npm run serve 命令配置这个也有这个特性 不同的是 在 package.json 的 scripts 中查找
4.关于 scripts 中命令配置的注意事项
	1.配置命令后 npm run xxx 对于 如果 命令别名位  start 可以省略 run, npm start 其他的必须  npm run starts
5.关于 cnpm
	1.淘宝的一个镜像平台 10分钟同步一次 npmmirror.com 只能下载 不能上传
	2.修改下载地址
		1.npm config set registry https://registry.npmmirro.com
		2.也可以安装  nrm 管理下载地址工具 
			1.nrm ls
			2.nrm use taobao
			3.修改 下载地址后 用 npm 比 cnpm  快
6.关于 yarn 的一些常用命令
	1.yarn init/yarn init y [初始化]
	2.yarn add uniq/yarn add uniq --dev/yarn global add nodemon [安装]
	2.yarn remove uniq/yarn global remove nodemon [删除]
	4.yarn [安装依赖]
	5.yarn 别名 不需要 run 
	6.yarn config set registry https://registry.npmmirror.co
	7.yarn config list 可以查看yarn配置
	8.锁文件 yarn.lock npm 为 package-lock.json
	9.yarn global 相关
		1.add 安装全局
		2.remove 删除全局
		3.upgrade 升级全局
		4.ls 查看全局安装
		5.bin 全局目录
		6.yarn global xxx
			1.如果不加 global 就是局部
7.关于 包 的发布 更新 和 删除
	1.注册 npmjs.com 账号 开发包后 执行命令 npm login 根据提示 填入用户名 密码 和 邮箱验证码
		1.注意 registry 的指向 必须是 npm 自己的镜像
	2.npm publish 发布完成后 出现 +tm_math 后表示发布成功
	3.升级 也就是 更改后的再次发布 必须 确保 版本号 在 package.json  中修改 要高于上次
	4.npm unpublish --force
8.nodejs 版本管理 nvm 下载地址 https://github.com/coreybutler/nvm-windows/releases 下载 nvm-steup.exe
	1.nvm list avialable 所有可以安装的
	2.nvm list 已经安装的
	3.nvm install latest 安装最新的
	4.nvm install 18.2.1 num uninstall 18.2.1
	5.nvm use 18.2.1
9.npm view 相关
	1.npm view packageName versions 查看这个包目前所有的在线版本
		1.npm ls packageName 查看当前包的 最新版本
	2.npm view packageName dependencies 查看这个包当前的依赖
package.json 内容详细
{
  "name": "packagetest",
  "version": "1.0.1",
  "description": "packagetest",
  "main": "index.js",
  "scripts": {
    "test": "echo \"hello\""
  },
  "repository": {
    "type": "git",
    "url": "no repository"
  },
  "keywords": [
    "packagetest"
  ],
  "author": "tm",
  "license": "ISC",
  "dependencies": {
    "uniq": "^1.0.1"
  },
  "devDependencies": {
    "math": "0.0.3"
  }
}

常见库

内置库

fs 文件库

1.判断是文件还是文件夹 代码如下
	1.stats.isDirectory() 2.stats.isFile()
点击查看代码
fs.stat('./fs.js', function (err, stats) {
    console.log(stats.isFile());         //true
})
		1.console.log(stats); 结果如下:
点击查看代码
{ dev: 16777220,
  mode: 33188,
  nlink: 1,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 40333161,
  size: 61,
  blocks: 8,
  atime: Mon Sep 07 2015 17:43:55 GMT+0800 (CST),
  mtime: Mon Sep 07 2015 17:22:35 GMT+0800 (CST),
  ctime: Mon Sep 07 2015 17:22:35 GMT+0800 (CST) }
2.读取文件使用代码如下:
点击查看代码
let fs = require("fs")
function readPromise(path){
    return new Promise((resole,reject) =>{
        fs.readFile(path,'utf-8',(error,data) =>{
            if(error) reject(error);
            resole(data);
        })
    });
}
3.写入文件
	1.fs.writeFile(file, data[, options], callback)
4.追加
	1.fs.appendFile(file,data,callback)
5.文件流写入 常用大文件写入
	1.let ws = fs.createWriteStream("./tt.js");ws.write("let a = 3;\r\n");ws.write("console.log(a);\r\n");ws.close();
6.大文件读取
	1.fs.createReadStream("../a.mp4"); rs.on("data",chunk => {console.log(chunk,chunk.length);})
	2.rs.on("end",() =>{console.log("read over");})
	3.复制文件的方法
		1.let data = fs.readFileSyn(path); fs.writeFileSyn(path1,data);
		2.rs.on("data",chunk => {ws.write(data)})
			1.优化写法  rs.pipe(ws) //  通过管子留出来
4.其他用法
	1.fs.rename(oldname,newname,err => {})
	2.fs.unlink(filename,err => {}) / fs.rm 同样可以删除
	3.fs.mkdir(filename,err => {})
		1.递归创建 fs.mkdir(filename,{recursive:true}, err => {})
	4.fs.readdir(filename,(err,data){//data 是个数组}
	5.fs.rmdir(path,{recursive:true},err => {}) 等价于 fs.rm(path,,{recursive:true},err => {})

formidable 库

1.新版本有时候出错 可以下载 下载多的版本 经典版本 "formidable": "2.1.2",
2.const form = formidable({ multiples: true });
	form.parse(req, (err, fields, files) => {
    	if (err) {
        	next(err);
        	return;
    	}
    	res.json({ fields, files });
		//这里 fields from form:text password radio checkbox [{"user":"wusong"}]
		//files 文件 [{"pic":{"size":13828,"filepath":"C:..\\Temp","newFilename":"3d4","mimetype":"image/jpeg","mtime":"","originalFilename":"00.jpg"}}]
	});
3.传文件后的路径 let url = req.protocol + "://" + req.get("host") + "/sources/images/" + files.pic.newFilename;
formidable 用法实例
formidable 传文件返回路径
const express = require("express")
const formidable = require("formidable")
const path = require("path")
const router = express.Router()
router.get("/", (req, res) => {
    res.render("portrait", { name: "燕子李三" })
})

router.post("/", (req, res) => {
    const form = formidable(
        {
            multiples: true,
            uploadDir: path.resolve(__dirname, "../sources/images"),
            keepExtensions: true,
            // allowEmptyFiles:false
        });
    form.parse(req, (err, fields, files) => {
        if (err) {
            next(err);
            return;
        }
        // res.json({ fields, files });
        let url = req.protocol + "://" + req.get("host") + "/sources/images/" + files.pic.newFilename;
        res.end(url)
    });

})
module.exports = router;
formidable 用法初体验和数据结构
const express = require("express")
const formidable = require("formidable")
const router = express.Router()
router.get("/", (req, res) => {
    res.render("portrait", { name: "燕子李三" })
})

router.post("/", (req, res) => {
    const form = formidable({ multiples: true });
    form.parse(req, (err, fields, files) => {
        if (err) {
            next(err);
            return;
        }
        res.json({ fields, files });
    });
    //仅仅为展示数据结构 无意义
    let localFieldsAndFiles = {
        "fields": {
            "user": "wusong"
        }, 
        "files": {
            "pic": {
                "size": 13828, 
                "filepath": "C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\1ff263b223d3d56c3b5f3e101", 
                "newFilename": "1ff263b223d3d56c3b5f3e101", 
                "mimetype": "image/jpeg", 
                "mtime": "2023-06-29T05:39:26.129Z", 
                "originalFilename": "00.jpg"
            }
        }
    }
})
module.exports = router;

nodemon 库 需要配合 esm 库

1.npm i nodemon 用来监听文件变化 并自动执行
2.但是 nodemon 不支持 es6 语法 运行就报错 怎么解决呢
	1.安装 esm 库  npm i esm
	2.在脚本中设置  "start": "nodemon -r esm basetest.js"
		1.加了 -r esm 参数 就不报错了 [前提必须保证 node 支持 es6 的语法] 比如node 12.22.6 版本 node 不支持 ?. 语法
		2.怎么调都无用 这个参数的 目的 应该是 node 帮助 nodemon  把 es6 解析成了 es5 它认识的语法
	3.但是并不能完全解决 因为 esm 库 四五年 没有更新了 最新的 语法仍然无法解析

shortid 库 生成字符串id

1.const shortid = require("shortid");
2. db.get("posts").push({id:shortid.generate(),...req.body}).write()

nanoid 库 生成字符串id

1.npm i nanoid
2.用法
	import { nanoid } from 'nanoid'
	model.id = nanoid() //=> "V1StGXR8_Z5jdHi6B-myT"

lodash 库 包含很多高阶函数 debounce throttle

1.npm i --save lodash
2.可以 按需引入
	var array = require('lodash/array');
	var object = require('lodash/fp/object');
3.也可以整个引入
	var _ = require('lodash');

nprogress 库 请求数据的时候 显示进度条 需要重新封装 axios 时候 加入的

swiper 库 pc 移动 轮播图

npm i swiper@5 --save

qrcode 库 把地址或者数据转换为 二维码图片 [微信支付]

1.npm i qrcode
	import QRCode from 'qrcode'
	// With promises
	QRCode.toDataURL('I am a pony!')
	  .then(url => {
		console.log(url)
	  })
	  .catch(err => {
		console.error(err)
	  })

	// With async/await
	const generateQR = async text => {
	  try {
		console.log(await QRCode.toDataURL(text))
	  } catch (err) {
		console.error(err)
	  }
	}

vee-validate 库 npm i validate@2 表达验证库 配合 vue3

1.作为插件 写在插件文件夹 下 插件文件名 vee-validate.js 内容如下
		import Vue from "vue"
		import VeeValidate from "vee-validate"
		import zh_CN from "vee-validate/dist/locale/zh_CN"
		console.log(zh_CN)
		Vue.use(VeeValidate)
		VeeValidate.Validator.localize("zh_CN",{
			messages:{
				//原来英文 配置后提示中文  
				// 比如 下面  attributes 配置 phone 值为 手啊机
						// 会提示 手啊机 格式无效
				// 如果不配置  phone 值为 手啊机
					// 会提示 phone 格式无效 之所以 他知道是 phone 是因为 input 里面有 name:"phone"
						1.配置什么 name 就显示什么 [还需要 在 errors.first("phone1") 也是 phone1 ]
					// Vue 文件 源码如下
					// <input type="text" placeholder="please input phone" name="phone1" v-model="phone" v-validate ="{required:true,regex:/^1\d{2}4$/}" />
					// <span class="error-msg">{{ errors.first("phone1") }}</span>
				// 如果 不配置  ...zh_CN.messages 会显示 The phone1 value is not valid 也就是说 配置了中文 就代替了英文
				//也就是说 messages 这里是配置 字段的 具体错误原因 [中英文 不配置英 配置中]
				// attributes 里面配置的是 name 对应的 中文 这样 name[中文] + 具体错误原因
				 ...zh_CN.messages,
			},
			attributes:{
				 phone:"手啊机"
			}
		})
2.在 main.js 直接引入 import "vee-validate"
3.在 vue 文件 from 表达中 直接使用
	<input type="text" placeholder="please input phone" name="phone1" v-model="phone" v-validate ="{required:true,regex:/^1\d{2}4$/}" /> <span class="error-msg">{{ errors.first("phone1") }}</span>
	2.验证方式 有哪些种类
		1.regex
		2.is [is:password] 这个 password 是前面定义的 name  这里的 name 是 password1 也就是两个密码得一样
		3.自定义规则 很方便啊
				//自定义规则
				VeeValidate.Validator.extend("myformat",{
					validate(value){
						if(value == "my"){
							return true;
						}else{
							return false;
						}
					},
					getMessage(field){
						// 如果 不符合格式 就是按这个 错误提示 field 就似乎 vue 中的 name 当然 如果在 attribute 中 有
							1.对应的中文 就显示 对应的中文
									attributes:{
										// phone:"手啊机"
										"custom1":"自定义个不行啊 靠 "
									}
							2.完整显示 自定义个不行啊 靠 | 格式有点儿不对头啊
						//自定义错误的 模式 
							1.phone|格式有点儿不对头啊
						return field + "| 格式有点儿不对头啊"
					}
				})
			2.vue  文件 使用 如下
  <input type="text" placeholder="custom1" name="custom1" v-model="custom1" v-validate ="{required:true,myformat:true}" /><hr/>
	<span class="error-msg">{{ errors.first("custom1") }}</span><hr/>
	1.提示结果 custom1| 格式有点儿不对头啊
		1.配置 attributes 可以显示 [自定义个不行啊 靠 | 格式有点儿不对头啊]
4.检测所有的 表单 是否全部通过
	async isAllValidate(){
	  let res = await this.$validator.validateAll()
	  console.log(res)
	},
5.也可以重新定义 is 等方法
		//可以 重写 里面原有的方法
		VeeValidate.Validator.extend("is",{
			validate(...res){
				console.log(res) 
				//res 是一个数组 arr[0] 是当前文本值 arr[1] 是一个数组 可以是多个数值
				// 多少个数值 取决于 传的值的 个数 v-validate ="{required:true,is:[username,phone]} 
					1.arr1 长度为 2 分别是 username,phone 的值
				if(res[0] == res[1][0] + "1" ){
					return true;
				}else{
					return false;
				}
			},
			getMessage(field){
				//自定义错误的 模式 phone|格式有点儿不对头啊
				return field + "| 格式有点儿不对头啊"
			}
		})
6.也可以自定义 新 的方法
		//自定义规则
		VeeValidate.Validator.extend("myformat",{
			validate(value){
				if(value == "my"){
					return true;
				}else{
					return false;
				}
			},
			getMessage(field){
				//自定义错误的 模式 phone|格式有点儿不对头啊
				return field + "| 格式有点儿不对头啊"
			}
		})
7. v-validate ="{required:true,is:[username,phone]}  这里传的参数 是 data 里面的属性 不是 元素的 name

connect-history-api-fallback 库 [解决不用hash模式 用 history 模式 刷新 404 问题]

1.npm i connect-history-api-fallback
2.在 app.use(express.static(__dirname +"/static")) 之前
	1.const history = require('connect-history-api-fallback');
	2.app.use(history())

animate.css 库 vue 可以用的动画库

1.npm i animate.css

pubsub-js 库 消息的订阅与发布

	1.使用步骤
		1. 安装库 npm i pubsub-js
		3.使用
			1.A想接受消息 A订阅
				1.引入库 import pubsub from "pubsub-js
				2.mouted(){this.pubid = pubsub.subscribe("hello"),this.methodDemo}
					1.methodDemo 参数 1 订阅名字 ”hello" 2 数据data
				3.最好在 beforeDestroy 中取消订阅
					1.pubsub.unsubscribe(this.pubid)
			2.B 想发消息
				1.引入库 import pubsub from "pubsub-js
				2.pubsub.publish("hello",data)

lowdb 操作 json文件作为数据库

1.npm i lowdb@1.0.0 经典版本
2.增删改查操作
	1.db.get("posts").value() 获取数据库 全部
		1.db.get("posts").remove({id:2}).value() //获取某条
	2.db.get("posts").remove({id:2}).write(); 根据条件删除 不一定根据id
	3.db.get("posts").find({id:2}).assign({title:"facaila"}).write() 更新某条数据
	4.db.get("posts").push({}).write()/.unshift
代码实例
lowdb 配合 esj 记账本 esj 部分
    <link rel="stylesheet" href="/stylesheets/account.css">
    <script src="/javascripts/account.js"></script>
</head>

<body>
    <div class="out">
        <ul>
            <li class='title'>
                <span class="amount">amount</span>
                <span class="comment">commnt</span>
                <span class="type">type</span>
                <span class="fn">x</span>
            </li>
            <% data.forEach(item=> {%>
                <li class='<%= item.type == 1 ?"earn":"pay"%>'>
                    <span class="amount">
                        <%= item.amount* parseInt(item.type) %>
                    </span>
                    <span class="comment">
                        <%= item.comment %>
                    </span>
                    <span class="type">
                        <%= item.type==1?"In":"Out"%>
                    </span>
                    <!-- 最方便的是外部包裹一层 a 标签 -->
                    <span class="fn" myid="<%= item.id %>">x</span>
                </li>
                <% }) %>
        </ul>
    </div>
</body>
lowdb 配合 esj 记账本 js部分
const low = require('lowdb')
const FileSync = require('lowdb/adapters/FileSync')
const express = require('express');
const {resolve} = require("path");
const shortid = require("shortid");

const adapter = new FileSync(resolve(__dirname,'../public/data/db.json'))
const db = low(adapter)

//初始化 json 文件
// db.defaults({ posts: [], user: {} })
//   .write()

const router = express.Router();

router.get('/', function(req, res, next) {
  let dbdata = db.get("posts").value()
  res.render('account', {data:dbdata});
});

//这个正常的添加放在上面 避免被捕获为 id
router.get('/add', function(req, res, next) {
  res.render('add', { title: 'Express' });
});

// 匹配 id 用来删除
router.get('/:id', function(req, res, next) {
//删除操作 根据id
  db.get('posts')
  .remove({id: req.params.id})
  .write()

  let dbdata = db.get("posts").value()
  res.render('account', {data:dbdata})
});

//添加也是add路径 但是 是 post
router.post('/add', function(req, res, next) {
  db.get('posts')
    .push({id:shortid.generate(),...req.body})
    .write()
  res.render("domsg",{msg:"ok success"})
});

module.exports = router;

moment 库

1.npm i momnet
2.常见年月日情况
	YYYY	2014	4 或 2 位数年份。 注意: strict 模式只能解析 4 位
	YY	14	2 位数年份
	Y	-25	带有任意数字和符号的年份
	Q	1..4	季度。 将月份设置为季度的第一个月。
	M MM	1..12	月份
	MMM MMMM	Jan..December	moment.locale() 设置的语言环境中的月份名称
	D DD	1..31	一个月中的第几天
	Do	1st..31st	带序号的月份中的第几天
	DDD DDDD	1..365	一年中的某一天
	X	1410715640.579	Unix 时间戳
	x	1410715640579	Unix 毫秒时间戳
3.常见时分秒
	H HH	0..23	小时(24 小时制)
	h hh	1..12	小时(与 a A 一起使用的 12 小时时间。)
	k kk	1..24	小时(24 小时制,从 1 到 24)
	a A	am pm	子午线前后(注意一个字符 a p 也被认为是有效的)
	m mm	0..59	分钟
	s ss	0..59	秒
	S SS SSS ... SSSSSSSSS	0..999999999	小数秒
	Z ZZ	+12:00	与 UTC 的偏移量为 +-HH:mm、+-HHmm 或 Z
4.常见使用
	console.log(moment()) //Moment<2023-07-01T11:27:21+08:00>
	console.log(moment().format("YYYY--MM-DD HH:mm-ss")) //2023--07-01 11:27-21
	console.log(+moment())//1688182041753
	console.log( moment("2022-02-01").diff(moment("2022-02-08"),"days"))//-7
	moment("20190221:212923","YYYYMMDD:HHmmss").utcOffset(480)//Moment<2019-02-21T21:29:23+08:00>
		1.如果不加 utcOffset(480) 结果是 //Moment<2019-02-21T13:29:23+00:00> 
		2.这个不一定 有时候不加也显示正常 Moment<2019-02-21T21:29:23+08:00>

express-generator 库

1.express程序生成器 npm i -g express-generator
2.生成 ejs 文件格式 命令 express -e destfolder
3.生成的目录结构
	1. ./bin/www 这个是执行入口 引入 app.js
		1.导入 app.js 后  app.set 设置端口
			1.var port = normalizePort(process.env.PORT || '999'); app.set('port', port);
				1.看 package.json 里面是否鹅湖之了 port 变量 没有的化 默认 999
	2. public 静态资源路径
	3. routers 模块化路由中间件 路径
	4. veiws *.ejs html 路径
	5. app.js 代码逻辑模块 最终导出 让 bin/www 引入

小的库

1.npm i md5 库
1.npm i cookie-parser
2.const cookieparser = require('cookie-parser')
	2.app.use(cookieparser()); 中间件会自动解析 cookie 并挂在在 req.cookies

jsonwebtoken 库 jwt 库

1.常用用法
	1.第一种用法最简单 加密后 返回一个 token 可以把这个token 通过 cookie 或者 header 发给 浏览器 并和 前端约定好 从哪里传过来
	2.这个token 当用户登录成功后 发送给客户端 可以通过以下几种方式 一般app多用 因为可以方便的头部带过来
		1.从头部
		2.cookie
		3.body
		4.query
	3.服务器拿到 token 后 用 verify 解析 这个token 有几种情况 说明不可用了
		1.解析失败 说明被篡改或者伪造了
		2.对比 { data: { foo: 'bar' }, iat: 1688303596, exp: 1688307196 } 中 1688307196 - 1688303596 就是 60 *60 过期时间
			取时间戳 只要 大于 exp: 1688307196 就过期了  iat: 1688303596 这个是生成token的时间
		3.如果 解析 成功 又没有过期 就说明通过了 可以验证 里面的内容了
		4.然后可以根据内容验证 做不同的工作
	4.可以在通过后 在 req.body 上挂上不同的数据 做成 中间件
		1.let token = jwt.sign({
			data:{name:"zhangsan"},
			exp: Math.floor(Date.now() / 1000) +  60*60
		},key)

		2.let token =  jwt.sign({
			 exp: Math.floor(Date.now() / 1000) + (60 * 60),
			data: { foo: 'bar' }
		}, key);

		  jwt.verify(token, key, function(err, decoded) {
			if(err){
				//如果验证通不过 解析失败走这里 说明伪造的
				console.log(111111111111)
				console.log(err)
				return;
			}
			//{ data: { foo: 'bar' }, iat: 1688303596, exp: 1688307196 }
			console.log(decoded) // bar
		  });

express-session 库 一般和 connect-mongo 库 配合使用

0.connect-mongo 库 需要 mongodb 库配合 node 12.22.6 不支持 部分 ES6语法 所以只适合于 mongodb 4.13.0
	1.先安装 connect-mongo 再安装 mongondb
	2.connect-mongo": "^4.6.0" 配合 mongodb 4.13.0 否则 安装 npm i md5 的时候 总提示这两个库不匹配
1.关于 express-session 的说明
	在使用express-session模块时,session数据默认保存在服务器端的内存中 每个客户端都会被分配一个唯一的session ID
	并且该session ID会被存储在客户端的cookie中 当客户端发送请求时,服务器会根据session ID来查找对应的session数据
	然而,将session数据保存在内存中存在一些限制 例如无法在多个服务器之间共享session数据
	以及当服务器重启时会丢失所有session数据 为了解决这些问题,可以使用其他存储引擎来替代默认的内存存储
	express-session模块支持多种存储引擎,包括:
		1.内存存储引擎(默认):将session数据保存在服务器的内存中
		2.文件存储引擎:将session数据保存在服务器的文件系统中
		3.数据库存储引擎:将session数据保存在数据库中,如MongoDB、MySQL等
		4.缓存存储引擎:将session数据保存在缓存中,如Redis、Memcached等
2.内存存储引擎(默认):将session数据保存在服务器的内存中 代码如下
	1.只要加入了 express-session 中间件 访问任何页面 只要cookie不带来 session id [错误 超时 也算没带来]
	2.服务器就会给浏览器分发一个新的 session id 这个 session id 可能没有 对应有用的信息 比如 res.session.login = true;
	3.一旦任何时候设置了 res.session.login = true; 这个新分发的 session id 就可以看到了
	4.也就是 id 错了就换 换了后 之前的 就无用了 永远第一时间分发给 客户端
	[重点] 当然 是否为丢失的 session 重新分配 这个有设置的
		1.saveUninitialized: false, //是否为每次请求都设置一个cookie用来存储 sessionid
		2.[但 如果 请求页面的 sessionid丢失 或者无效 是否给你更新 为 true 更换 为 false 只有设置 sesssion.name 时]
express-session in memory
const express = require('express');
const session = require('express-session');

const app = express();

// 使用express-session中间件
app.use(session({
  secret: 'mysecretkey', // 用于加密session数据的密钥
  resave: false, // 是否在每次请求时重新保存session数据 续上一杯酒 更新时间 只要访问就加 20 类似防抖
  saveUninitialized: true, // 是否自动保存未初始化的session数据
  cookie: { secure: false } // 设置cookie的安全选项 如果为 true http 协议不发送session
}));

// 定义路由
app.get('/', (req, res) => {
  // 设置session数据 只要在这里定义 同时 req 的 cookie 中 没有对应的 session id 
  // 就会通过 header 的 cookie 给浏览器 session id
  req.session.username = 'John';
  req.session.age = 30;
  //{"cookie":{"originalMaxAge":null,"expires" :null,"secure":false,"httpOnly":true,"path":"/"},"username":"John","age":30}
  res.send(req.session); 
});

app.get('/user', (req, res) => {
    // 只要设置了 session 并且没有被 destroy 只要浏览器带着对应的 session id 就可以看不到
    //如果发现 sessionid 不对 不存在 就给分发一个新的 但是 这个session id 对应的 对象上
    //没有任何数据 需要设置 req.session.username 后 这个 新分配的 session id 才能看到
    res.send(req.session) ;
});
app.get('/destroy', (req, res) => {
    //destroy 之前
    //{"cookie":{"originalMaxAge":null,"expires" :null,"secure":false,"httpOnly":true,"path":"/"},"username":"John","age":30}
    req.session.destroy();
    //destroy 之后
    //{"cookie":{"originalMaxAge":null,"expires":null,"secure":false,"httpOnly":true,"path":"/"}}
  res.send(req.session) ;
});

// 启动服务器
app.listen(999, () => {
  console.log('Server started on port 999');
});

生成器 目录结构 分析代码实例
record_account_mongoose_session 主路由分析
const low = require('lowdb')
const FileSync = require('lowdb/adapters/FileSync')
const express = require('express');
const { resolve } = require("path");
const shortid = require("shortid");
const md5 = require("md5")
const AccountModel = require("../models/AccountModel")
const UserModel = require("../models/UserModel")
const AuthVerifyMiddleWare = require("../middleware/AuthVerifyMiddleWare")



const adapter = new FileSync(resolve(__dirname, '../public/data/db.json'))
const db = low(adapter)

//初始化 json 文件
// db.defaults({ posts: [], user: {} })
//   .write()

const router = express.Router();

router.get("/login", (req, res, next) => {
  if(req.session.name){
    return res.redirect("/account")
  }
  res.render("login");
})

router.post("/login", (req, res, next) => {
  UserModel.findOne({ ...req.body, password: md5(req.body.password) }).exec((err, data) => {
    if (err) {
      console.log(err)
      return res.render("domsg", {
        msg: "login server something wrong",
        url: "/account/login",
        urlText: "try again",
        flag: "failure"
      })
    }
    console.log(data)
    if (data) {
      req.session.name = data.username;
      req.session._id = data._id;
      res.render("domsg", {
        msg: "login successful",
        url: "/account",
        urlText: "go center",
        flag: "success"
      })
    } else {
      res.render("domsg", {
        msg: "username or password wrong",
        url: "/account/login",
        urlText: "try again",
        flag: "failure"
      })

    }
  })
})

router.get("/reg", (req, res, next) => {
  if(req.session.username){
    return res.redirect("/account");
  }

  res.render("reg");
})

router.post("/reg", (req, res, next) => {
  UserModel.create({ ...req.body, password: md5(req.body.password) }, (err, data) => {
    if (err) {
      console.log(err)
      return res.render("domsg", {
        msg: "reg server something wrong",
        url: "/account/reg",
        urlText: "try again",
        flag: "failure"
      })
    }
    console.log(data)
    res.render("domsg", {
      msg: "reg successful",
      url: "/account/login",
      urlText: "login",
      flag: "success"
    })
  })
})


router.get('/', AuthVerifyMiddleWare, function (req, res, next) {
  AccountModel.find().exec((err, data) => {
    if (err) {
      return res.render("domsg", {
        msg: "list server wrong",
        url: "/account",
        urlText: "try again",
        flag: "failure"
      })
    } else {
      res.render('account', { data: data });
    }
  })
  // let dbdata = db.get("posts").value()
});

//这个正常的添加放在上面 避免被捕获为 id
router.get('/add',AuthVerifyMiddleWare, function (req, res, next) {
  res.render('add');
});

// 匹配 id 用来删除
router.get('/:id',AuthVerifyMiddleWare, function (req, res, next) {
  AccountModel.remove({ _id: req.params.id }).exec((err, data) => {
    if (err) {
      console.log(err)
      res.status(500).send("remove failure")
    } else {
      AccountModel.find().exec((err, data) => {
        if (err) {
          console.log(err)
          res.status(500).send("list failure")
        } else {
          // console.log(data)
          res.render('account', { data: data })
        }
      })
    }
  })
  //删除操作 根据id
  // db.get('posts')
  // .remove({id: req.params.id})
  // .write()
  // let dbdata = db.get("posts").value()
  // res.render('account', {data:dbdata})

});

//添加也是add路径 但是 是 post
router.post('/add',AuthVerifyMiddleWare, function (req, res, next) {
  AccountModel.create(req.body, (err, data) => {
    if (err) {
      console.log(err)
      return res.render("domsg", {
        msg: "add server something wrong",
        url: "/account/add",
        urlText: "try again",
        flag: "failure"
      })
    }
    console.log(data)
    res.render("domsg", {
      msg: "add successful",
      url: "/account",
      urlText: "go center",
      flag: "success"
    })
  })

  // db.get('posts')
  //   .push({id:shortid.generate(),...req.body})
  //   .write()
});

module.exports = router;

app.js 代码分析
const createError = require('http-errors');
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
//日志管理
const logger = require('morgan');

//引入 路由中间件 [小app]
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');

const {dbhost,dbport,dbname,secret} = require("./config/config")

const app = express();

//session  设置
const session = require('express-session');
const MongoStore = require('connect-mongo');

// 使用express-session中间件
app.use(session({
  secret: secret, // 用于加密session数据的密钥
  resave: true, // 是否在每次请求时重新保存session数据 续上一杯酒 更新时间 只要访问就加 20 类似防抖
  saveUninitialized: true, //是否为每次请求都设置一个cookie用来存储 sessionid [但请求页面的 sessionid丢失 或者无效 是否给你更新 为 true 更换 为 false 只有设置 sesssion.name 时]
  cookie: { 
    secure: false,//如果为 true http 协议 不给浏览器发 session 必须是https才发
    httpOnly:false,//为true 浏览器 通过 document.cookie 为 "" 通过名字 就知道 必须只有通过 http 才可以
    maxAge:1000*20 //如果不加 默认是 浏览器关闭 失效
   },
  // store: new MongoStore({ url: 'mongodb://localhost/session' })
  store: MongoStore.create({
    mongoUrl: `mongodb://${dbhost}:${dbport}/${dbname}`,
    // mongoOptions: advancedOptions // See below for details
  })
}));


// view engine setup
app.set('views', path.join(__dirname, 'views'));
// 设置渲染引擎为 ejs
app.set('view engine', 'ejs');
// 日志开发级别的
app.use(logger('dev'));

//设置post body 解析中间件 类型 body-parser
app.use(express.json());
app.use(express.urlencoded({ extended: false }));

//设置cookie解析
app.use(cookieParser());
//设置网站静态资源 根目录 js css 之类的都去这里找 html 结构的 *.ejs 去views 文件夹找
app.use(express.static(path.join(__dirname, 'public')));


//如果输入根目录 转到 /account 目录
app.get("/",(req,res) =>{
  res.redirect("/account")
})

//加载 全局路由中间件
app.use('/account', indexRouter);
// app.use('/users', usersRouter);

//没有人理会的 小可怜路径 都来这里 
// catch 404 and forward to error handler
app.use(function(req, res, next) {
  res.status(403).send("<h1>Page Not Found</h1>");
  // next(createError(404)); // 原始的错误处理方式 暴露的信息过多
})

//有人理 但是发生了某种错误 走这里处理 比如 chunk 类型没有转换 不合适啊之类的
// error handler
app.use(function(err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error');
});

module.exports = app;


express 库

基本知识
1.获取路由参数
	1.app.get("/:id.html",(req,res)=>{req.params.id}) 127.0.0.1/999.html  req.params.id 就是 999
2.响应头的一些设置
	1.兼容 http 的一些写法
		1.res.statusCode = 200 res.statusMessage = "OK" res.setHeader("set-cookie",["a","b"]) res.write("a") res.end("b")
	2.express 自己的方法
		1.res.status(200) res.set("set-cookie",["a"]) res.send("响应体不乱码")
		2.链式操作 res.status(200).set("set-cookie","a").send("滚犊子")
		3.其他响应
			1.res.redirect("http://www.baidu.com")重定向
			2.res.download("./abc.jpg") 下载响应
			3.res.json("package.json") json 响应
			4.res.sendFile(__dirname + "/abc.html") 响应文件内容
中间件的用法
1.对于定义中间来说没有全局和局部之分 这是在引用的时候 有全局和局部之分
	0.定义中间件 let middleware = (req,res,next) => {res.auth = 'ok';next()}
	1.全局中间件 app.use(middleware)
	2.路由中间件 app.get("/home",middle,(res,req) => {})
2.静态资源中间件 app.use(express.static(__dirname + "/user")); // 访问的所有资源都从 user文件夹下找
	1.app.get("/",(req,res) => {app.sendFile("index.html");})
	2.静态中间件设置后 访问根目录 默认会找 index.html  如果此时 app.get("/") 返回的不是 index.html 那么中间件和路由
	3.谁在前面 谁说了算 [路由响应动态资源  静态资源中间件 响应静态资源]
3.防盗链
	1.let referer = req.get(referer) if(referer){let {hostname} = new URL(referer)}; 
	2.console.log(hostname)// 127.0.0.1 此时不带端口
4.404 的处理方式汇总
	1.app.get("*",(req,res) => {})
	2.app.use(function(req,res,next){next(createError(404))}) //等于是生成了一个中间件 next的时候 抛出错误
5.app.use(middleware) //这种middleware的路径是参照网站根目录
	1.app.use('/users', usersRouter); 这种中间件 的路径是参照 /users 如果在 userRouter 中 .get("/users/a")
	2.1./users 是可以省略的 带参照路径的 app.use 里面的中间件都是在这个路径的基础上
路由中间件的模块化
0.[分身的 let app = exprss() 就是 let router = express.router()]
1.const router = express.Router();调用 express 的静态方法 Router 生成一个类似 app 的路由中间件
	1.这和普通 let middleware = (req,res,next){} 比较 更方便模块化 管理 就像 body-parser 一样 独立出去 逻辑页面就简单了
2.router.get("/",(req,res) => {res.send("内容")});module.exports = router; [页面为 adminRouter.js]
3.let adminRouter = require("./adminRoouter") let app = express(); app.use(adminRouter); [主页面内容 index.js]
常见的中间件
1.body-parser 解析 form 表单 body 数据 并挂在 res.body 以对象的形式
	0.const bodyParser = require('body-parser');
	1.let urlencodedMiddleWare =  bodyParser.urlencoded({ extended: false})//解析普通 form 表单数据
		1.app.use(urlencodedMiddleWare)//应用 全局中间件
	2.let jsonMiddleWare = bodyParser.json()//解析 json 表单数据中间件
基本用法代码实例
body-parser 中间件用法
const express  = require("express")
const bodyParser = require('body-parser');


// parse application/x-www-form-urlencoded
let urlencodedMiddleWare =  bodyParser.urlencoded({ extended: false })
// parse application/json
// let jsonMiddleWare = bodyParser.json()
//用这个中间件后 会在 req.body 上 挂上 post提交回来的数据
const app = express()
//定义中间件 本身没有全局 路由之分 看用在哪里
let authed = (req,res,next) => {
    res.auth = "ok";
    next()
}
app.use(urlencodedMiddleWare)
//静态资源中间件 作为全局来设置 设置后 后面 html网页中牵涉到的 image css js 都会从这里来查找
app.use(express.static(__dirname + "/user"));

app.get("/",(req,res) => {
    app.sendFile("index.html");
})

app.get("*",(req,res) => {
    //用了全局中间后 这里也会输出ok 这是我们不希望看到的
    res.status(404).set("auth","no").end(`<h1>no auth ${res.auth}</h1>`) //<h1>no auth undefined</h1>
})

app.post("/",(req,res) => {
    res.json(req.body);
})

app.listen(999,() => {
    console.log("server on 999")
})
中间件的 全局 和 路由
const express  = require("express")
const app = express()
//定义中间件 本身没有全局 路由之分 看用在哪里
let authed = (req,res,next) => {
    res.auth = "ok";
    next()
}

//把中间件用作局部
app.get("/home",authed,(req,res) => {
    res.end("home " + res.auth) // home ok
})
app.get("/admin",authed,(req,res) => {
    res.end("admin " + res.auth) // admin ok
})

app.get("*",(req,res) => {
    //用了全局中间后 这里也会输出ok 这是我们不希望看到的
    res.status(404).set("auth","no").end(`<h1>no auth ${res.auth}</h1>`) //<h1>no auth undefined</h1>
})

//把中间件用作全局
app.use(authed);
app.get("/home",(req,res) => {
    res.end("home " + res.auth) // home ok
})
app.get("/admin",(req,res) => {
    res.end("admin " + res.auth) // admin ok
})

app.get("*",(req,res) => {
    //用了全局中间后 这里也会输出ok 这是我们不希望看到的
    res.status(404).set("auth","no").end(`<h1>no auth ${res.auth}</h1>`) //<h1>no auth ok</h1>
})


app.listen(999,() => {
    console.log("server on 999")
})
express最基本用法和 req 结构
const express = require("express")
const app = express()
app.get("/home",(req,res) => {
    console.log(req.get("host"))//127.0.0.1:999
    console.log(req.method)//GET
    console.log(req.url)///home?keywords=shoes&price=361
    console.log(req.ip)//::ffff:127.0.0.1
    console.log(req.path)///home
    console.log(req.query)//{ keywords: 'shoes', price: '361' }
    console.log(req.headers)
    //仅用于展示 无意义
    let headers = {
        host: '127.0.0.1:999',
        connection: 'keep-alive',
        'cache-control': 'max-age=0',
        'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
        'upgrade-insecure-requests': '1',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
        accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
        'sec-fetch-site': 'none',
        'sec-fetch-mode': 'navigate',
        'sec-fetch-user': '?1',
        'sec-fetch-dest': 'document',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'en,zh-CN;q=0.9,zh;q=0.8',
        cookie: 'cookie2'
      }
    res.end("home")
})
app.all("/reg",(req,res) => {
    res.end("reg");
})
app.listen(999,() => {
    console.log("serer on 999")
})
//无实际意义 仅用于展示 req 的内部结构
let request = {
    headers: {
        host: '127.0.0.1:999',
        connection: 'keep-alive',
        'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
        'upgrade-insecure-requests': '1',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
        accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
        'sec-fetch-site': 'none',
        'sec-fetch-mode': 'navigate',
        'sec-fetch-user': '?1',
        'sec-fetch-dest': 'document',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'en,zh-CN;q=0.9,zh;q=0.8',
        cookie: 'cookie2'
      },
    baseUrl: '',
    originalUrl: '/home?keywords=shoes&price=361',
    _parsedUrl: {
      search: '?keywords=shoes&price=361',
      query: 'keywords=shoes&price=361',
      pathname: '/home',
      path: '/home?keywords=shoes&price=361',
      href: '/home?keywords=shoes&price=361',
      _raw: '/home?keywords=shoes&price=361'
    },
    params: {},
    query: { keywords: 'shoes', price: '361' }
}

json-server 库 和 restful api 标准

1.restful api 规范
	1.新增 post /song
	2.删除 delete /song/10
	3.修改 [全部] put /song/10  [部分] patch /song/10
	4.获取所有 /get /song
	5.获取单个 get /song/10
2.json-server // npm i -g json-server
	1.安装服务后 新建立一个 db.json 文件名字可以随意 内容如下
		{
			"posts": [
			  { "id": 1, "title": "json-server", "author": "typicode" }
			],
			"comments": [
			  { "id": 1, "body": "some comment", "postId": 1 }
			],
			"profile": { "name": "typicode" }
		}
	2.然后执行 json-server --watch db.json 后 会自动得到以下的地址
		  http://localhost:3000/posts
		  http://localhost:3000/comments
		  http://localhost:3000/profile
	3.访问任何一个 按照 restful api 的规范 都可以进行 增删改查
3.常用来 模拟 增删改查的 工具
	1.apipost
		1.https://www.apipost.cn/
		2.可以很方便的制作接口
			1.放在同一个目录 可以方便添加各种参数
	2.postman

ejs 库

1.注意事项
	1.{xyj:xyj0} 原始引用数据 就算一样 也不能合并为 xyj
	2.xyj 是 ejs 引用的key xyj0 这个可以不一样 xyj1 也行
	3.ejs.render('<?= users.join(" | "); ?>', {users: xyj},{delimiter: '?'});
		1.默认 <%%>  可以改为 任意标签
		2.也可以自定义 全局分隔符 ejs.delimiter = '$';
2.常见代码控制语句
	1.<% arr.forEach((item) => {%> 这里是重复 <%= item %> 的代码 <% }) %>
		1.注意插值语句 需要  %=  控制语句不需要 =
	2.<% if (user) { %><h2> <%= user.name %> </h2><% }else{%> <h3>我是</h3><% } %>
3.ejs 文件中 可以使用所有的 js 代码
	1.对于非原生 js  可以渲染的时候 作为 对象 或者方法传过去 就可以和在js中 一样的使用 去操作影响 ejs 的表现结果
常见代码实例
express 使用 ejs
const ejs = require("ejs")
const fs = require("fs")
const express = require("express");
const path = require("path")
//原始数据
const xyj = ["孙悟空","猪八戒","沙和尚","唐僧"]
const app = express();
//设置引擎和views路径
app.set("view engine","ejs")
app.set("views",path.resolve(__dirname,"./views"))
//为了能够解析 xyj.ejs 中的 css 必须设置 静态路由中间件
app.use(express.static(__dirname + "/views"))
app.get("/",(req,res) =>  {
//直接调用 xyj.ejs 模板 和 数据 扩展名可以省略
    res.render("xyj",{xyj:xyj})
})

app.listen(999,() => {
    console.log("server start 999")
})

//xyj.ejs content
<head>
    <link rel="stylesheet" href="./css/xyj.css">
</head>
<!-- body 中的 格式 -->
<body>
    <ul>
        <% xyj.forEach(item => { %>
        <li><%= item %></li>
        <% }) %>
    </ul>
</body>

初体验 西游记
<!-- body 中的 格式 -->
<body>
    <ul>
        <% xyj.forEach(item => { %>
        <li><%= item %></li>
        <% }) %>
    </ul>
</body>

const ejs = require("ejs")
const fs = require("fs")
//原始数据
const xyj = ["孙悟空","猪八戒","沙和尚","唐僧"]
//读入 ejs.html
let html = fs.readFileSync(__dirname + "/ejs.html")
//html读出来是 chunk 数据 需要 toString 然后渲染
let res = ejs.render(html.toString(),{xyj:xyj})
//res 就是渲染后的数据
<body>
    <ul>
        <li>孙悟空</li>
        <li>猪八戒</li>
        <li>沙和尚</li>
        <li>唐僧</li>
    </ul>
</body>
console.log(res)

process 库

1.let process = require("process"); proces.memoryUsage() // 返回内存使用的对象

path 库

1.path.resolve(__dirname,"./a.js")
2.path.sep //路径分隔符 windows \ linux /
3.path.basename(path) //获取文件名 t.js
4.path.dirname(path) //文件夹 路径 c:\a\b
5.path.extname(path) //扩展名 .js

http 库

1.常见写法代码如下 [包含 url.parse new URL headers 数据格式]
const fs = require("fs")
const path = require("path")
const http = require("http");
const url = require("url");

const server = http.createServer((request, respone) => {
    let requestBody = "";
    respone.setHeader("content-type", "text/html;charset=utf-8")
    // console.log(request.method)//GET
    // console.log(request.httpVersion)//1.1
    // console.log(request.url)///search?keywords=abc
    // console.log(request.headers)
    let urlParse = url.parse(request.url,true);
    // console.log(urlParse);
    console.log(url.parse(request.url).pathname)//search
    console.log(url.parse(request.url, true).query) //{ keywords: 'abc' }
    path.extname("/index.html")//.html
    request.on("data", (chunk) => {
        requestBody += chunk;
    })

    request.on("end", () => {
        console.log("receive data is:") 
        console.log(requestBody) //user=wang 当post提交时候的数据
        respone.end("http ok 混合")
    })
})
    //headers 这里json仅仅为了展示 headers 常见内容
    let headers = {
        host: '127.0.0.1:999',
        connection: 'keep-alive',
        'cache-control': 'max-age=0',
        'sec-ch-ua': '"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"',
        'sec-ch-ua-mobile': '?0',
        'sec-ch-ua-platform': '"Windows"',
        'upgrade-insecure-requests': '1',
        'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36',
        accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
        'sec-fetch-site': 'none',
        'sec-fetch-mode': 'navigate',
        'sec-fetch-user': '?1',
        'sec-fetch-dest': 'document',
        'accept-encoding': 'gzip, deflate, br',
        'accept-language': 'en,zh-CN;q=0.9,zh;q=0.8'
    }
    //无效赋值 仅仅为了展示 url解析后的结构
    urlParse =  {
        protocol: null,
        slashes: null,
        auth: null,
        host: null,
        port: null,
        hostname: null,
        hash: null,
        search: '?keywords=abc&name=zs&age=23',
        query: { keywords: 'abc', name: 'zs', age: '23' },
        pathname: '/search',
        path: '/search?keywords=abc&name=zs&age=23',
        href: '/search?keywords=abc&name=zs&age=23'
      }
// let url1 = new URL("/search?keywords=abc&name=zs&age=23","http://127.0.0.1:999")
//仅仅是为了展示
url1 = {
    href: 'http://127.0.0.1:999/search?keywords=abc&name=zs&age=23',
    origin: 'http://127.0.0.1:999',
    protocol: 'http:',
    username: '',
    password: '',
    host: '127.0.0.1:999',
    hostname: '127.0.0.1',
    port: '999',
    pathname: '/search',
    search: '?keywords=abc&name=zs&age=23',
    searchParams: { 'keywords' => 'abc', 'name' => 'zs', 'age' => '23' },
    hash: ''
  }
  //url1.searchParams.get("keywords") //这里获取属性内容 需要用 get方法 原始对象是 => 形式的都需要 get 获取
  //:冒号形式的用 . 获取即可
server.listen(999, (...rest) => {
    console.log("server start on 999")
})
/2.login页面返回 登录 /reg 页面返回 注册
const server = http.createServer((request,respone) => {
    //如果 请求是 GET 页面来自 /login 返回 注册页面 如果页面来自 /reg 返回 注册页面
    let {method} = request;
    let {pathname} = url.parse(request.url,true)
    let res = "";
    respone.setHeader("content-type","text/html;charset=utf-8");
    // console.log(method,pathname,request.method,request.url)
    if(method == "GET" && pathname == "/login")
    {
        res = "登录页面"
    }else if(method == "GET" && pathname == "/reg")
    {
        res  = "注册页面"
    }else{
        res = "其他页面"
    }
    respone.end(res)
})
server.listen(999,() => {
    console.log("server start on 999")
})
生成一个表格并返回[资源服务器]
const server = http.createServer((request,response) => {
    //可以随意的改变根目录
    let root = path.resolve(__dirname,"./tableTest");
    console.log(root)
    let {pathname} = url.parse(request.url)
    console.log(pathname)
    if(pathname == "/"){
        //不同的类型 设置的 content-tppe 是不同的 比如 js css 设置成 这个就不行
        response.setHeader("content-type","text/html;charset=utf-8")
        console.log(root +"/index.html");
        readPromise(root +"/index.html").then(data => {
            response.end(data);
        }).catch(error => {
            response.end("no found")
        })
    }else{
        readPromise(path.resolve(root + pathname)).then(data => {
            response.end(data);
        }).catch(error => {
            response.end("no found")
        })
    }

})
server.listen(999,() => {
    console.log("server start 999")
})
3.可以写任何返回头部
	1.对于返回头部相同数据不同的 如cookie: respone.setHeader("set-cookie",["cookie1","cookie2"])
	2.response.statusCode = 200; response.stateMessage = "OK"
4.可以多次 response.write("data1");response.write("data2");response.end() 一起返回
5.天坑系列
	1.这个头只能在返回网页 时候加上 response.setHeader("content-type","text/html;charset=utf-8")
		1.如果网页中附带有 js css 请求 加上就完蛋喽
6.相对路径
	1. ./js/ajs 等价于 js/a.js 都属于当前目录
	2. /js/a.js 绝对路径 和 host 拼接即可

杂项

会话控制

###cookie
	1.express 框架 对于 cookie 的操作
		1.res.cookie("name","zhangsan").send("ok");
			1.浏览器 表现 rosponse headers:Set-Cookie:name=zhangsan; Path=/ 
			2.浏览器关闭后 cookie 消失
		2.res.cookie("name","zhangsan").cookie("work","teacher",{maxAge:5*1000})
			1.浏览器 表现 Set-Cookie:work=teacher; Max-Age=5; Path=/; Expires=Sat, 01 Jul 2023 14:15:54 GMT
			2.5秒生存期 5 秒后自动销毁
		3. res.clearCookie("name").json({status:"ok"}) //删除cookie
			1.浏览器 表现 Set-Cookie:name=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT
				1.设置 name 为空 设置 过期时间为 计算机元年
	2.用 cookie-parser 库 中间件 可以解析 cookie 并挂在在 req.cookies;
###session
	1.session 流程
		1.账号密码校验通过后 创建 session 然后通过响应头 cookie 把 对应的 session id 传给浏览器
			1.session 可以存储在 数据库中
		2.浏览器下次活动 带着 session id 在cookie中 
		3.服务器通过session id 对比数据库中的 数据 来校验用户是否已经登录
###token

mime类型

1.mime [multiplepurpose internet mail extensions]
2.type/subType text/html image/jpeg image/png application/json
3.常见
	1.html: text/html
	2.css: text/css
	3.js: text/javascript
	4.png: image/png
	5.jpg: image/jpeg
	6.gif: image/gif
	7.mp4: video/mp4
	8.mp3: audio/mp3
	9.json: application/json 
	10.form 常见 [enctype 属性的 数值]
		1.application/x-www-form-urlencoded
			1.form method 设置为 post 后 默认 enctype 就是这个属性值 可以省略
		2.application/json
		3.application/octet-stream
			1.如果 response 包头 设置后  server to client //有下载效果
			2.client to server 上传文件 也是这个类型 [formidable 解析后 能清楚看到]
		3.multipart/form-data
			1.如果上传文件必须设置为这个 如果不设置上传 body 内容是  user=wusong&pic=00.jpg
			2.有这个标记就不一样 user: wusong pic: (binary)
multipart/form-data body 体
------WebKitFormBoundaryTIIY1AhvmLhrwAA0
Content-Disposition: form-data; name="user"

wusong
------WebKitFormBoundaryTIIY1AhvmLhrwAA0
Content-Disposition: form-data; name="pic"; filename="00.jpg"
Content-Type: image/jpeg

------WebKitFormBoundaryTIIY1AhvmLhrwAA0--
		4.text/plain
		5.

容易忽略的问题

1.不管  module.exports = fn 或者 module.exports = {fn} let a/b/c = require("./module") 可以赋值给任何变量名字express-generator 库
posted @ 2023-06-26 16:03  闭区间  阅读(33)  评论(0编辑  收藏  举报