电子商务网站设计与建设,创建网站需要哪些要素,宝应人网站论坛,phpstudy wordpressnode.js 学习笔记
node.js服务端笔记文档学会写接口#xff0c;path、包、模块化、fs、express、中间件、JWT、开发模式、cors。 gitee#xff1a;代码接口笔记
1什么是node.js
nodejs 是基于ChromeV8#xff0c;引擎的一个javaScript 运行环境。node.js 无法使用DOM和BO…node.js 学习笔记
node.js服务端笔记文档学会写接口path、包、模块化、fs、express、中间件、JWT、开发模式、cors。 gitee代码接口笔记
1什么是node.js
nodejs 是基于ChromeV8引擎的一个javaScript 运行环境。node.js 无法使用DOM和BOM的浏览器进行操作。
fs模块
1 fs文件系统模块 Fs 模块是node.js 官方提供的用来操作文件的模块他提供了一系列的方法和属性用来满足用户对文件的操作需求。 1、readFile.radeFile()方法用来读取指定文件的内容。
const fs require(fs);// 参数1 是读取文件夹的路径类型是String类型。
// 参数2 读取文件的采用的编码格式一般为utf8格式。
// 参数3 是一个回调函数
// 回调函数第一参数是错误信息如果null表示读取成功了没有错误信息。一般判断null表示文件读取成功。
// 回调函数第二参数是文件内容如果读取失败返回 undefined否则读取文件内容成功。
fs.readFile(../File/文件.txt,utf8,(res, data) {console.log(32455,res)if(res null) {console.log(读取文件成功)console.log(324,data)}
})。2、readFile.writFile() 方法用来向指定文件中写入内容的。 fs.writeFile 没有文件则进行创建文件但是如果创建的路径中没有文件夹则会创建失败有文件就进行追加内容这个方法只能创建文件不能用来创建路径。 如果要在js中使用fs模块需要引入模块
const fs require(fs)// 但是如果创建的路径中没有文件夹则会创建失败 [Error: ENOENT: no such file or directory, open ../File1/我爱你1.txt]
// 如果重复调用 writeFile 方法新的内容会吧原来的内容给覆盖掉。
// 参数一是读取文件的路径类型是String 必填选项。
// 参数二是往文件里写入的内容。
// 参数三是回调函数。// 回调函数参数第一个是返回的错误信息。默认返回null表示文件写入成功没有错误信息。回调第二个参数是undefined。fs.writeFile(../File1/我爱你1.txt,我不爱你真的加的,(res) {console.log(243,res)
})3、__dirname解决路径拼接错误 在使用fs模块处理路径的时候。 如果使用相对路径会发生路径拼接错误的一些问题。原因就是在你去执行代码的时候会执行node命令时所处的路径动态拼接出操作文件的完整路径。 __dirname 表示当前文件夹所处的路径直接从盘符开始算起。
// cd ../ 返回上一层文件再去运行会报错
// Error: ENOENT: no such file or directory, open ./1.txt
// 因为nodejsnote 文件里没有 ../File/文件.txt// 解决方案就是不要使用相对路经进行拼接直接给出完整的一个路径。直接从盘符开始算起他就没有这个相对路径拼接的一个问题了。因为你执行的时候他就相对于当前文件去找他对应的一个文件。const fs require(fs);
// __dirname 表示当前文件夹所处的路径直接从盘符开始算起。
console.log(4343,__dirname)fs.readFile(${__dirname}/1.txt,utf8,(res, data) {console.log(32455,res)if(res null) {console.log(读取文件成功)console.log(324,data)}
})path模块 path 模块是nodejs官方提供用来处理路径的,他提供了一系列的方法和属性用来满足用户对于路径的处理。path.join([…path]) 用来将多个路径完整的拼接成一个字符串。path.basename(path,[ext]) 可以获取路径中的最后一部分方法用来从路径字符串中将中文名解析出来path.extname() 用来获取路径中的扩展名部分 const fs require(fs);
const path require(path)
// 表示当前文件夹所处的路径直接从盘符开始算起。
// ${__dirname}../fs/1.txt [Error: ENOENT: no such file or directory, open /Users/sunzhihao/Desktop/nodejsnote/path../fs/1.txt]// ${__dirname}../fs/1.txt path.join(__dirname,../fs/1.txt)console.log(4343,path.join(__dirname,../fs/1.txt)) // /Users/sunzhihao/Desktop/nodejsnote/fs/1.txtfs.readFile(path.join(__dirname,../fs/1.txt),utf8,(res, data) {console.log(32455,res)if(res null) {console.log(读取文件成功)console.log(324,data)}
})// 看输出
const fpath /a/b/c/index.html
console.log(433,path.basename(fpath)) // 433 index.html
console.log(466,path.basename(fpath,.html)) // 466 index
console.log(444,path.extname(fpath)) // 444 .html
http模块 http模块由nodejs提供用来创建web服务的通过http提供的http.createServer()方法可以把一台电脑变为web服务器对外部提供web资源。 1、什么是ip地址 ip地址指的是我们每台计算机的唯一地址IP地址具有唯一性类似于手机号只有知道ip地址才能进行数据通信 IP地址格式通常用点分十进制。形成a.b.c.d 的性质都是0-255的十进制。 互联网中的每台web服务器都有自己的IP的地址可以输入 ping www.baidu.com 命令可以查到百度服务器的IP地址 域名和Ip地址能够唯一标记网络上的计算机单IP地址是一长串数字不直观不好记于是发明了字符行的地址方案叫域名 Domain Name 地址 IP地址和域名是一一对应的关系。 域名服务器就是提供IP地址和域名之间的转换服务的服务器 127.0.0.1 locallhost 2、端口号 计算机的端口号就好像是现实生活中的门牌号一样通过门牌号外卖小哥可以把东西准确的送到你的手中。 一台电脑闹可以运行非常多的服务每个web服务都有一个唯一的端口号客户端发送过来的网络请求通过端口号可以准确的交给对应的web服务进行处理。 每个端口号不能同时被多个web服务占用。 url中的80端口可以省略 3、创建http服务
怎么启动服务 node index.js node 加上文件名字就可以了
//引入我们的 http 模块
const http require(http)
// 创建web 服务器
const server http.createServer()// 为服务实例绑定 request 事件监听客户的请求
server.on(request, ( req, res) {// const url req.url// const method req.method// 为了防止中文乱码设置响应头 鎮ㄧ殑url鏄/璇锋眰鏂规硶鏄疓ETres.setHeader(Content-Type,text/html; charsetutf-8)// console.log(start server,url,method)// // 将内容返回给客户端// res.end(您的url是${url}请求方法是${method})let content h1404 Not found/h1console.log(url,req.url)if(req.url / || req.url /index.html) {content h1首页/h1}else if(req.url /about) {content h1about/h1}res.setHeader(Content-Type,text/html; charsetutf-8)res.end(content)
})// 启动服务
server.listen(8080,() {console.log(server running at http://127.0.0.1:8080)
})4、防止中文乱码设置响应头
// 为了防止中文乱码设置响应头 鎮ㄧ殑url鏄/璇锋眰鏂规硶鏄疓ET
server.on(request, ( req, res) {res.setHeader(Content-Type,text/html; charsetutf-8)
}什么是包
1、包的概念
Nodejs 中的第三方模块又叫包 相当于别名 电脑等于计算机包是是第三方开发出来免费供别人使用由于node js 的内置模块仅提供了一些底层的API、导致基于内置模块开发慢效率低包是基于内置模块封装出来的提供了更高级的更方便的api提高开发效率 国外有一家IT公司叫做 npm inc 这家公司有个著名网站http://www.npmjs.com/ 他是全球最大的包共享平台。 npm i moment 安装时间格式化的包
const moment require(moment) 导入需要的包
moment().format(YYYY--MM-DD HH:mm:ss)2、第一次安装包多了哪些文件 node_modules 文件用 来存放已经安装到项目中的包require 导入第三方包就是从这个目录开始查找并加载包的。 package-lock.json 配置文件用来记录 node_modules 目录下的每一个包的下载信息列入包的名字版本号下载地址。 3、安装指定版本的包 通过符号指定版本 npm install moment2.0.0 4、包的版本号 是由 点分十进制定义的 2.0.0 第一位大版本 第二位 功能修复 第三位 bug 修复版本 只要版本号赠长了后面版本号就是 0 5、快速创建package.json npm包管理工具提供了一个快捷命令可以在执行命令时所处的目录中快速创建package.json这个包管理 npm init -y 注意 ①上述命令只能在英文的目录下成功运行所以项目文件夹的名称一定要使用英文命名不要使用中文不能出现空格。 ②运行npm install命令安装包的时候npm包管理工具会自动把包的名称和版本号记录到package.json中。 npm规定在项目根目录中必须提供一个叫做package.json的包管理配置文件。用来记录与项目有关的一些配置 信息。例如 ·项目的名称、版本号、描述等 ·项目中都用到了哪些包 ·哪些包只在开发期间会用到 ·那些包在开发和部署时都需要用到 6、npm install命令 可以运行npm install命令或npmi)一次性安装所有的依赖包 1/执行npm install命冷时npm包管理工具会先读取package.json中的dependencies节点 2/读取到记录的所有依赖包名称和版本号之后npm包管理工具会把这些包一次性下载到项目中 3 npm install 卸载包 npm uninstall moment 注意npm uninstall命令执行成功后会把卸载的包自动从package.json的dependencies中移除掉。 如果安装慢可以使用淘宝镜像 npm install --registryhttps://registry.npm.taobao.org 当然也可以将淘宝镜像下载到本地 7、devDependencies节点 如果某些包只在项目开发阶段会用到在项目上线之后不会用到则建议把这些包记录到devDependencies节点中。 与之对应的如果某些包在开发和项目上线之后都需要用到则建议把这些包记录到dependencies节点中。 您可以使用如下的命令将包记录到devDependencies节点中 安装指定的包并记录到devDependencies节点中 npm 包名 -D 注意上述命冷是简写形式等价于下面完整的写法 4 npm install 包名 --save-dev npm install moment 默认会安装到里面 dependencies 里面开发和项目上线都会用到 package.json dependencies 节点 专门介绍了安装了哪些包 如果你使用了moment包但是没有安装会报错 error : cannot find module moment 8、项目包 那些被安装到项目的node modules目录中的包都是项目包。 项目包又分为两类分别是 开发依赖包被记录到devDependencies节点中的包只在开发期间会用到 核心依赖包被记录到dependencies节点中的包在开发期间和项目上线之后都会用到 1 npm i 包名 -D 开发依赖包会被记录到devDependencies节点下 2 npm i 包名 核心依赖包会被记录到dependencies节点下】 9、全局包 在执行npm install命令时如果提供了-g参数则会把包安装为全局包。 全局包会被安装到C.\Users用户目录AppData\Roaming\npm\node modules目录下。 1 npm i包名-g #全局安装指定的包 2 npm uninstall包名-g 卸载全局安装的包 注意 ①只有工具性质的包才有全局安装的必要性。因为它们提供了好用的终端命令。 10、规范的包结构 在清楚了包的概念、以及如何下载和使用包之后接下来我们深入了解一下包的内部结构。 一个规范的包它的组成结构必须符合以下3点要求 ① 包必须以单独的目录而存在 ② 包的顶级目录下要必须包含package.json这个包管理配置文件 ③package.json中必须包含name,version,main这三个属性分别代表包的名字、版本号、包的入口。 注意以上3点要求是一个规范的包结构必须遵守的格式关于更多的约束可以参考如下网址 https://yarnpkg.com/zh-Hans/docs/package-json 模块化
模块化的概念 模块化指的是解决一个复杂的问题时候从顶层向下逐层把系统分成若干模块的过程对于整个系统来说模块可以租合可以分解可以更换的单元。 在代码中模块化就是遵守固定的规则把一个大文件拆成独立独立相互依赖的多个小模块。 好处 提高代码的可维护性 可复用性 按需加载 如果大家都遵循同样的模块化代码规范可以降低大家之间的沟通成本极大方便了各个模块的相互调用。 模块化的规范指的就是 使用什么样的方式向外暴露成员 使用什么样的语法格式来引用模块。 模块化又分为 内置模块 内置模块是nodejs提供的例如 fs path http 自定义模块 用户创建的每个.js文件称为自定义模块 第三方模块 由第三方开发出来的模块并非官方提供的也不是自己创建的使用的话需下载 加载模块 加载模块使用require()方法可以加载需要的内置模块用户自定义模块第三方模块 使用require() 方法加载其他模块时会执行被加载模块重的代码 const fs require(‘fs’) 加载内置模块 const fs require(‘./index.js’) 加载用户自定义模块 使用require方法可以省略js后缀名 const _ require(‘lodash’) 加载第三方模块 模块化作用域 和函数作用域类似在自定义模块中定义的变量方法只能在当前模块中访问无法被外界访问这种模块级别的访问限制叫做模块化作用域。 module 对象 每个.js 自定义模块都有一个module对象它里面存储了和当前模块有关信息 node.js 遵循了commonjs模块规范化commonjs 规定了模块的特性和各模块之间如何相互依赖 每个模块内部module变量代表当前模块 module变量是一个对象 它的 exports 属性 等于 module.exports 是对外的接口 require 是用来加载模块的
console.log(module)
// 打印
Module {id: .,path: /Users/sunzhihao/Desktop/nodejsnote/模块化,exports: {},parent: null,filename: /Users/sunzhihao/Desktop/nodejsnote/模块化/模块化的基本概念.js,loaded: false,children: [],paths: [/Users/sunzhihao/Desktop/nodejsnote/模块化/node_modules,/Users/sunzhihao/Desktop/nodejsnote/node_modules,/Users/sunzhihao/Desktop/node_modules,/Users/sunzhihao/node_modules,/Users/node_modules,/node_modules]
}向外共享模块作用域中的成员
// module.exports 在自定义模块可以通过 module.exports 将模块内的成员给共享出去共外界使用。
// 在外界使用require方法导入自定义模块时得到的就是module.export 所指的对象。// 文件1 代码
module.exports.aa 我是数据
// 文件2 代码
const a require(./1)
console.log(a) // { aa: 我是数据 }// 共享成员的注意点 导入的结果永远以 module.exports 指向的对象为准
// 文件1 代码
module.exports.aa 我是数据
module.exports {bb:333
}
// 文件2 代码const a require(./1)
console.log(a) // { aa: 我是数据 }exports 对象
// 由于module.exports写的复杂为了简化成员node提供了exports对象
// module.exports 和 exports指向的同一个对象 结果永远以 module.exports 指向的对象为准// 文件1 代码
exports.aa 我是数据// 文件2 代码
const a require(./1)
console.log(a) // { aa: 我是数据 }模块的加载机制
内置模块的加载机制 内置模块是由Node.js官方提供的模块内置模块的加载优先级最高。 例如require(fs)始终返回内置的fs模块即使在node modules目录下有名字相同的包也叫做fs。 自定义模块的加载机制 使用require加载自定义模块时必须指定以./或…/开头的路径标识符。在加载自定义模块时如果没有指定./或…/ 这样的路径标识符则node会把它当作内置模块或第三方模块进行加载。 同时在使用require导入自定义模块时如果省略了文件的扩展名则Node.js会按顺序分别尝试加载以下的文件 ①按照确切的文件名进行加载 ②补全js扩展名进行加载 ③补全json扩展名进行加载 ④补全node扩展名进行加载 ⑤加载失败终端报错 第三方模块的加载机制 如果传递给require 的模块标识符不是一个内置模块也没有以’./”或’…/开头则Node.js会从当前模块的父 目录开始尝试从/node modules文件夹中加载第三方模块。 如果没有找到对应的第三方模块则移动到再上一层父目录中进行加载直到文件系统的根目录。 例如假设在’C.Users\itheima\project\foo.js’文件里调用了require(tools),则Node.js会按以下顺序查找 ①C:\Users\itheima\project\node modules\tools ②C:\Users\itheima\node modules\tools ③C:\Users\node modules\tools 4C:\node modules\tools 找不到报错 目录作为模块 当把目录作为模块标识符传递给require 进行加载的时候有三种加载方式 ①在被加载的目录下查找一个叫做package.json的文件并寻找main属性作为require加载的入口 ②如果目录里没有package.json文件或者main入口不存在或无法解析则Node.js将会试图加载目录下的index.js文件。 ③如果以上两步都失败了则Node.js会在终端打印错误消息报告模块的缺失Error:Cannot find module ‘Xx’ {
name: pack,
version: 1.0.0,
description: ,
main: index.js,
scripts: {
test: echo \Error: no test specified\ exit 1
},
keywords: [],
author: ,
license: ISC,
dependencies: {
moment: ^2.29.4
}}main 设置aaa.js 找对应文件夹里的 /aaa.js
没有设置 main 默认找 index.js
注意 json 中不能使用单引号Express 服务端框架
什么是express 官方给出的概念Express是基于Node.js平台快速、开放、极简的Web开发框架。 通俗的理解Express的作用和Node.js内置的http模块类似是专门用来创建Web服务器的。 Express的本质就是一个npm上的第三方包提供了快速创建Web服务器的便捷方法。 Express的中文官网http:lwww.expressjs.com.cnl 进一步理解Express 思考不使用Express能否创建Web服务器 答案能使用Node.js提供的原生http模块即可。 思考既生瑜何生亮有了http内置模块为什么还有用Express)? 答案http内置模块用起来很复杂开发效率低Express是基于内置的http模块进一步封装出来的能够极大的提高开发效率。 思考http内置模块与Express是什么关系 答案类似于浏览器中Neb API和jQuery的关系。后者是基于前者进一步封装出来的。 Express能做什么 对于前端程序员来说最常见的两种服务器分别是 Web网站服务器专门对外提供Web网页资源的服务器。 AP接口服务器专门对外提供API接口的服务器。 使用Express,我们可以方便、快速的创建Web网站的服务器或API接口的服务器。 安装nodemon 在终端中运行如下命令即可将nodemon安装为全局可用的工具 npm install -g nodemon 使用nodemon 当基于Node.js编写了一个网站应用的时候传统的方式是运行node app.js命令来启动项目。这样做的坏处是 代码被修改之后需要手动重启项目。 现在我们可以将node命令替换为nodemon命令使用nodemon app.js来启动项目。这样做的好处是代码 被修改之后会被nodemon监听到从而实现自动重启项目的效果。 node app.js 将上面的终端命令替换为下面的终端命令即可实现自动重启项目的效果 nodemon app.js 创建express定义get接口
创建文件夹
npm init -y
{name: demo,version: 1.0.0,main: index.js,scripts: {dev: nodemon index.js},keywords: [],author: ,license: ISC,description: ,dependencies: {body-parser: ^1.20.1,express: ^4.18.2,nodemon: ^2.0.20}
}npm install
启动 npm run dev
index.js 创建服务
const express require(express)
const app express()// 前端问号拼接参数
app.get(/user, (req, res) {// 前端访问 http://localhost/user?id232423kk9999// req.query 可以接收到前端传来的参数默认是一个空对象console.log(req.query) // { id: 232423, kk: 9999 }// res.send 向前端发送数据res.send({ name: get, age: 20 })
})//获取URL中的动态参数
app.get(/banner/:id/:name, (req, res) {// 前端访问 url http://localhost/banner/123/szh//通过req.params对象可以访问到URL中通过匹配到的动态参数console.log(req.params) // { id: 123, name: szh }// res.send 向前端发送数据res.send({ name: get, age: 20 })
})app.listen(80, () { // 监听80端口console.log(启动成功 http://localhost)
})创建express定义post接口
const express require(express)
const app express()
app.post(/list, (req, res) {res.send({ name: post, age: 20 })
})
app.listen(80, () { // 监听80端口console.log(启动成功 http://localhost)
})创建express定义post接口获取参数undefined
使用内置中间件解决注意json属性名一定要加上双引号
const express require(express)
const app express()//注意除了错误的中间件其他的中间件必须在路由之前进行配置app.use(express.json()) // 不配置req.body 拿不到json数据app.use(express.urlencoded({extended:false})) // 可以解析form-data的数据url-encode格式的数据app.post(/list, (req, res) {// req.body 可以拿到前端传过来的请求体 json 格式的console.log(23234, req.body) // undefined// 如果默认不配置解析表单数据的中间件则req.body 值为 undefined// 可以解析json但是无法解析url-encode格式的数据 form-data 格式的数据 不是那种对象包裹的数据// 使用中间件app.use(express.urlencoded({extended:false}))res.send({ name: post, age: 20 })
})app.listen(80, () {console.log(启动成功 http://172.0.0.1)
})也可以使用第三方的中间件解决req.body 值为 undefined
npm i body-parser
body-parser: ^1.20.1,
const bodyParser require(body-parser)
app.use(bodyParser.urlencoded({extended:false}))托管静态资源
express.static() express提供了一个非常好用的函数叫做express.static通过它我们可以非常方便地创建一个静态资源服务器 例如通过如下代码就可以将public目录下的图片、CSS文件、JavaScript文件对外开放访问了 app.use(express.static(public)) 现在你就可以访问public目录中的所有文件了 http://localhost:3000/images/bg.jpg http://localhost:3000/css/style.css http://localhost:3000/js/login.js 注意Express在指定的静态目录中查找文件并对外提供资源的访问路径。 因此存放静态文件的目录名不会出现在URL中。 托管多个静态资源目录 如果要托管多个静态资源目录请多次调用express…static0函数 访问静态资源文件时express.static函数会根据目录的添加顺序查找所需的文件。 public 文件找不到就去找file文件 app.use(express.static(‘./public’)) app.use(express.static(‘./file’)) http://localhost/index1.html 可以访问 托管静态资源挂载路径前缀 如果希望在托管的静态资源访问路径之前挂载路径前缀则可以使用如下的方式 app.use(‘/public’,express.static(‘./public’)) 现在你就可以通过带有/public前缀地址来访问public目录中的文件了 http://localhost:3000/public/images/kitten.jpg http://localhost:3000/public/css/style.css http://localhost:3000/public/js/app.js http://localhost/public/index1.html 路由的概念 3.Express中的路由 在Express中路由指的是客户端的请求与服务器处理函数之间的映射关系。 Express中的路由分3部分组成分别是请求的类型、请求的URL地址、处理函数格式如下 1 app.METHOD(PATH,HANDLER) 使用router路由模块 模块化路由 为了方便对路由进行模块化的管理Express不建议将路由直接挂载到app上而是推荐将路由抽离为单独的模块。 将路由抽离为单独模块的步骤斁如下 ①创建路由模块对应的js文件 ②调用express.Router()函数创建路由对象 ③向路由对象上挂载具体的路由 ④使用module.exports向外共享路由对象 ⑤使用app.use()函数注册路由模块 router.js
const express require(express)
var router express.Router() // 使用路由而不是 expressrouter.get(/getList/list,function (req,res) {res.send(get list success)
})router.get(/getBanner/list,function (req,res) {res.send(get banner success)
})module.exports router // 通过module对象将router抛出去Index.js
const express require(express)
const app express()const getList require(./router)
app.use(getList) // app.use 用来注册路由// app.use(/api, getList) 如果想要有统一前缀第一个参数// 访问就要在路径前面加上 /qpi http://localhost/api/getBanner/listapp.listen(80, () {console.log(启动成功 http://172.0.0.1)
})中间件
什么是中间件 中间件指的是中间处理业务部分的逻辑 Express中间件的调用流程 当一个请求到达Express的服务器之后可以连续调用多个中间件从而对这次请求进行预处理。 请求 中间件1 中间件2 处理响应 处理完毕响应 请求客户端 中间件的概念
Express的中间件本质上就是一个function处理函数Express中间件的格式如下
next函数是实现多个中间件连续调用的关键它表示把流转关系转交给下一个中间件或路由。
app.post(/list, (req, res, next) {
res.send({name:post,age:20})
})注意中间件函数的形参列表中必须包含next参数。而路由处理函数中只包含req和res。
中间件的作用 多个中间件之间共享同一份req和res。基于这样的特性我们可以在上游的中间件中统一为req或res对象添 加自定义的属性或方法供下游的中间件或路由进行使用。 局部生效的中间件
const express require(express)
const app express()
const middleware function (req, res, next) {console.log(我是局部中间件)// 处理逻辑next() // 调用表示可以执行下面这个接口
}app.get(/user, middleware, (req, res) {res.send({ a: 局部中间件 })
})
app.listen(80, () {console.log(启动成功 http://172.0.0.1)
})
定义多个局部中间件
// 可以在路由中通过如下两种等价的方式使用多个局部中间件
//以下两种写法是完全等价的可根据自己的喜好选择任意一种方式进行使用
const mw1 function (req, res, next) {console.log(我是局部中间件1)// 处理逻辑next() // 调用表示可以执行下面这个接口
}
const mw2 function (req, res, next) {console.log(我是局部中间件2)// 处理逻辑next() // 调用表示可以执行下面这个接口
}
// 定义一个数组传入中间件函数
app.get(/mw1,mw2,(req,res)res.send(Home page.)}
app.get(/,[mw1,mw2],(req,res){res.send(Home page.)定义全局中间
定义全局中间件中间件成功就会进行下一步
中间件相当于vue 的路由拦截
const express require(express)
const app express()
const mw function (req, res, next) {
console.log(第一个中间件)
// next() 不使用next() 报错 textStatus: timeout连接网络时发生异常 http://localhost/api/getBanner/list
// 通过req可以添加属性和方法 让下一个接口使用
let time new Date().getTime()
req.time timenext()
}
app.use(mw) // 注册全局中间件中间件传递数据
const express require(express)
const app express()const mw function (req, res, next) {
let time new Date().getTime()
req.time timenext()
}
// list 接口
app.post(/list, (req, res) {console.log(可以拿到中间件添加的属性, req.time) // 可以拿到中间件添加的属性 1675137876666res.send({ name: post, age: 20 })
})app.use(mw) // 注册全局中间件app.listen(80, () {console.log(启动成功 http://172.0.0.1)
})定义多个全局中间件
可以使用app.use 连续定义多个全局中间件。客户端请求到达服务器之后会按照中间件定义的先后顺序依次进行
调用示例代码如下
const express require(express)
const app express()
// 可以定义第二个中间件 放到第一个中间件的下面即可
app.use((req, res, next) {
console.log(第二个中间件)
next()
})了解中间件的5个使用注意事项 ①一定要在路由之前注册中间件 ②客户端发送过来的请求可以连续调用多个中间件进行处理 ③执行完中间件的业务代码之后不要忘记调用next0函数 ④为了防止代码逻辑混乱调用next0函数后不要再写额外的代码 ⑤连续调用多个中间件时多个中间件之间共享req和res对象 中间件的分类 为了方便大家理解和记忆中间件的使用Express官方把常见的中间件用法分成了5大类分别是 ①应用级别的中间件 ②路由级别的中间件 ③错误级别的中间件 ④Express内置的中间件 ⑤第三方的中间件 1.应用级别的中间件 通过app.use或app.get或app.post,绑定到app实例上的中间件叫做应用级别的中间件. 2.路由级别的中间件 绑定到express.Router(实例上的中间件叫做路由级别的中间件。它的用法和应用级别中间件没有任何区别。只不 过应用级别中间件是绑定到app实例上路由级别中间件绑定到router实例上. 3.错误级别的中间件
错误级别中间件的作用专门用来捕获整个项目中发生的异常错误从而防止项目异常崩溃的问题。
格式错误级别中间件的function处理函数中必须有4个形参形参顺序从前到后分别是error,req,res,next)。
注意错误中间件必须放在所有的接口最下面执行其他中间件可以放到最上面。
const express require(express)
const app express()// 接口代码 报错会被错误级别的中间件 捕获 而不会让整个项目无法启动
app.get(/error, (req, res) {throw new Error(服务器错误)res.send({ a: 24324 })
})app.use(function (error, req, res, next) {console.log(error.message, error)res.send({ error: error.message })// 服务器错误next()
})
app.listen(80, () {console.log(启动成功 http://172.0.0.1)
})4.Express内置的中间件 自Express4.16.0版本开始Express内置了3个常用的中间件极大的提高了Express项目的开发效率和体验 ①express…static快速托管静态资源的内置中间件例如HTML文件、图片、CSS样式等无兼容性 ②express.json解析JSON格式的请求体数据有兼容性仅在4.16.0版本中可用 ③express.urlencoded解析URL-encoded格式的请求体数据有兼容性仅在4.16.0版本中可用 配置解析application./json格试数据的内置中间件 2 app.use(express.json()) 配置解析application/x-www-form-urlencoded格式数据的内置中间件 4 app.use(express.urlencoded({extended:false }) 5.第三方的中间件是别人编写的你来使用。
也可以使用第三方的中间件解决req.body 值为 undefined
npm i body-parser
无法解析req.body可以替换
第三方的中间件
非Express官方内置的而是由第三方开发出来的中间件叫做第三方中间件。在项目中大家可以按需下载并配置
第三方中间件从而提高项目的开发效率。
例如在express(4.16.0之前的版本中经常使用body-parser这个第三方中间件来解析请求体数据。使用步
骤如下
①运行npm install body-parser安装中间件
②使用require导入中间件
③调用app.use0注册并使用中间件body-parser: ^1.20.1,
const bodyParser require(body-parser)
app.use(bodyParser.urlencoded({extended:false}))http请求
http请求分为三部分请求行请求头 请求体 请求行 是请求方法GET和POST是最常见的HTTP方法除此以外还包括DELETE、HEAD、OPTIONS、PUT、TRACE。 为请求对应的URL地址它和报文头的Host属性组成完整的请求URL。 是协议名称及版本号。 请求头 HTTP的报文头报文头包含若干个属性格式为“属性名:属性值”服务端据此获取客户端的信息。 与缓存相关的规则信息均包含在header中 请求体 报文体它将一个页面表单中的组件值通过param1value1param2value2的键值对形式编码成一个格式化串它承载多个请求参数的数据。不但报文体可以传递请求参数请求URL也可以通过类似于“/chapter15/user.html? param1value1param2value2”的方式传递请求参数。 常见的请求头 Accept 览器支持的 MIME 媒体类型, 比如 text/html,application/json,image/webp,/ 等比如text/plain只支持文本的数据 Accept-Encoding: 浏览器发给服务器,声明浏览器支持的编码类型gzip, deflate Accept-Language: 客户端接受的语言格式,比如 zh-CN Connection: keep-alive , 开启HTTP持久连接 Host服务器的域名 Origin告诉服务器请求从哪里发起的仅包括协议和域名 CORS跨域请求中可以看到response有对应的headerAccess-Control-Allow-Origin Referer告诉服务器请求的原始资源的URI其用于所有类型的请求并且包括协议域名查询参数 很多抢购服务会用这个做限制必须通过某个入来进来才有效 User-Agent: 服务器通过这个请求头判断用户的软件的应用类型、操作系统、软件开发商以及版本号、浏览器内核信息等 风控系统、反作弊系统、反爬虫系统等基本会采集这类信息做参考 Cookie: 表示服务端给客户端传的http请求状态,也是多个keyvalue形式组合比如登录后的令牌等 Content-Type HTTP请求提交的内容类型一般只有post提交时才需要设置比如文件上传表单提交等 响应头
报文头包含若干个属性 格式为“属性名:属性值”
常见的响应头 Allow: 服务器支持哪些请求方法 Content-Length: 响应体的字节长度 Content-Type: 响应体的MIME类型 Content-Encoding: 设置数据使用的编码类型 Date: 设置消息发送的日期和时间 Expires: 设置响应体的过期时间,一个GMT时间表示该缓存的有效时间 cache-control: Expires的作用一致都是指明当前资源的有效期, 控制浏览器是否直接从浏览器缓存取数据还是重新发请求到服务器取数据,优先级高于Expires,控制粒度更细如max-age240即4分钟 Location表示客户应当到哪里去获取资源一般同时设置状态代码为3xx Server: 服务器名称 Transfer-Encodingchunked 表示输出的内容长度不能确定静态网页一般没基本出现在动态网页里面 Access-Control-Allow-Origin: 定哪些站点可以参与跨站资源共享 Content-type
用来指定不同格式的请求响应信息俗称 MIME媒体类型
常见的取值 text/html HTML格式 text/plain 纯文本格式 text/xml XML格式 image/gif gif图片格式 image/jpeg jpg图片格式 image/pngpng图片格式 application/jsonJSON数据格式 application/pdf pdf格式 application/octet-stream 二进制流数据一般是文件下载 application/x-www-form-urlencodedform表单默认的提交数据的格式会编码成keyvalue格式 multipart/form-data 表单中需要上传文件的文件格式类型 CORS跨域资源共享
接口的跨域问题 刚才编写的GET和POST接口存在一个很严重的问题不支持跨域请求。 只要协议 域名 端口有一个不一致就会发生跨域。 解决接口跨域问题的方案主要有两种 ①CORS(主流的解决方案推荐使用) ②JSONP(有缺陷的解决方案只支持GET请求) 使用cors中间件解决跨域问题
cors是Express的一个第三方中间件。通过安装和配置cors中间件可以很方便地解决跨域问题。
使用步骤分为如下3步
①运行npm install cors安装中间件
②使用const corsrequire(cors)导入中间件
③在路由之前调用app.use(cors())配置中间件const cors require(cors) // cors解决跨域问题
app.use(cors())什么是CORS CORS(Cross-Origin Resource Sharing)跨域资源共享由一系列HTTP响应头组成这些HTTP响应头决定 浏览器是否阻止前端JS代码跨域获取资源。 浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如果接口服务器配置了CORS相关的HTTP响应头 就可以解除浏览器端的跨域访问限制。 CORS的注意事项 ①CORS主要在服务器端进行配置。客户端浏览器无须做任何额外的配置即可请求开启了CORS的接口。 ②CORS在浏览器中有兼容性。只有支持XMLHttpRequest Level2的浏览器才能正常访问开启了CORS的服 务端接口例如IE10、Chrome4、FireFox3.5)。 JSONP的概念与特点 概念浏览器端通过CORS响应头部
CORS响应头部Access–Control-Allow-Origin
// 5.CORS响应头部-Access-Control-Allow-Origin
// 如果指定了Access--Control-Allow-Origin字段的值为通配符*表示允许来自任何域的请求示例代码如下
res.setHeader(Access-Control-Allow-Origin,*)// 全局中间件
app.use(function (req,res,next) {res.setHeader(Access-Control-Allow-Origin,*)res.setHeader(Access-Control-Allow-Headers,Content-Type,X-Custom-Header)next()
})CORS响应头部-Access-Control-Allow-Headers 默认情况下CORS仅支持客户端向服务器发送如下的9个请求头 Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width、 Content-Type(值仅限于text/plain、multipart/form-data、application./x-www-form-urlencoded三者之一) 如果客户端向服务器发送了额外的请求头信息则需要在服务器端通过Access-Control–Allow-Headers对额外 的请求头进行声明否则这次请求会失败 允许客户端额外向服务器发送Content-Type请求头和X-Custom-Header请求头 注意多个请求头之间使用英文的逗号进行分割 res.setHeader(Access-Control-Allow-Headers,Content-Type,X-Custom-Header)
CORS响应头部- Access-Control-Allow-Methods
默认情况下CORS仅支持客户端发起GET、POST、HEAD请求。
如果客户端希望通过PUT、DELETE等方式请求服务器的资源则需要在服务器端通过 Access-Contro-Alow-Methods
来指明实际请求所允许使用的HTTP方法。
示例代码如下
只允许POST、GET、DELETE、HEAD清求方法res.setHeader(Access-Control-Allow-Methods,POST,GET,DELETE,HEAD)允许所有的HTTP清求方法res.setHeader(Access-Control-Allow-Methods,*)同时满足以下两大条件的情求就属于简单请求 ①请求方式GET、POST、HEAD三者之一 ②HTTP头部信息不超过以下几种字段 无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width、 Content-Type(只有三个值application/x-www-form-urlencoded, multipart/form-data, text/plain) 预检请求 公只要符合以下任何一个条件的请求都需要进行预检请求 ①请求方式为GET、POST、HEAD之外的请求Method类型 ②请求头中包含自定义头部字段 ③向服务器发送了application/json格式的数据 在浏览器与服务器正式通信之前浏览器会先发送OPTION请求进行预检以获知服务器是否允许该实际请求所以这一 次的OPTION请求称为“预检请求”。服务器成功响应预检请求后才会发送真正的情求并目携带真实数据。 简单请求的特点客户端与服务器之间只会发生一次请求。 预检请求的特点客户端与服务器之间会发生两次请求OPTION预检请求成功之后才会发起真正的请求。 delete 走两遍请求
Request Method: OPTIONS
Request Method: DELETE
Jwt
安装JWT相关的包 运行如下命令安装如下两个JWT相关的包 npm install jsonwebtoken express-jwt 其中 jsonwebtoken用于生成JWT字符串 express-jwt用于将JWT字符串解析还原成JSON对象 “jsonwebtoken”: “^8.5.1”, “escook/express-joi”: “^1.1.1” 什么是JWT WT(英文全称JSON Web Token)是目前最流行的跨域认证解决方案。 总结用户的信息通过Token字符串的形式保存在客户端浏览器中。服务器通过还原Tokn字符串的形式来认证用户的身份。 JWT的组成部分 JWT通常由三部分组成分别是Header(头部)、Payload(有效荷载)、Signature(签名)。 JWT的三个部分各自代表的含义(token 的组成) JWT的三个组成部分从前到后分别是Header、Payload、Signature。 其中 Payload部分才是真正的用户信息它是用户信息经过加密之后生成的字符串。 ·Header和Signature是安全性相关的部分只是为了保证Token的安全性。 JWT的web使用方式
客户端收到服务器返回的JWT之后通常会将它储存在localStorage或sessionStorage中。
此后客户端每次与服务器通信都要带上这个WT的字符串从而进行身份认证。推荐的做法是把JWT放在HTTP
请求头的Authorization字段中格式如下
Authorization: Bearer token
JWT服务端使用方式
const express require(express)
const app express()// 安装并且导入JWT的两个包
const jwt require(jsonwebtoken)
const expressJWTfn require(express-jwt)// 定义secret密钥
const secretKey woshimima// 解密
// 将JWT字符串还原为JSON对象
// 客户端每次在访问那些有权限接口的时候都需要主动通过请求头中的Authorization字段将Token字符串发
// 送到服务器进行身份认证。
// 此时服务器可以通过express-jwt这个中间件自动将客户端发送过来的Token解析还原成SON对象
// 1使用app.use()来注册冲间件
// 2 expressJWT({secret:secretKey})就是用来解析Token的中间件
// 3 unless({path:[/^\/api\/]})用来指定哪些接口不需要访问权限
// 4 app.use(expressJWT({secret:secretKey }).unless({path:[/a\/api\//]})
// 注意 只要使用expressJWT中间件就可以解析出来用户对象挂在到req.user身上// 使用 .unless({ path: [/^\/api\//] }) 指定哪些接口不需要进行 Token 的身份认证
app.use(expressJWT({ secret: jwtSecretKey }).unless({ path: [/^\/api\//] }))/*.在登录成功后生成JWT字符串调用jsonwebtoken包提供的sign方法将用户的信息加密成JWT字符串响应给客户端参数1用户的信息对象参数2加密的秘钥参数3配置对象可以配置当前token的有效期 30s 1h*/// 注意不要把密码加入到token加密中const tokenStr jwt.sign({ username: req.body.username }, secretKey, { expiresIn: 30s })
res.send({ success: 登陆成功, code: 200, token: tokenStr })开发模式
服务端渲染的Web开发模式 服务端渲染的概念服务器发送给客户端的HTML页面是在服务器通过字符串的拼接动态生成的。因此客户端不 需要使用Ajax这样的技术额外请求页面的数据。 服务端渲染的优缺点 优点 ①前端耗时少。因为服务器端负责动态生成HTML内容浏览器只需要直接渲染页面即可。尤其是移动端更省电。 ②有利于SEO。因为服务器端响应的是完整的HTML页面内容所以爬虫更容易爬取获得信息更有利于SEO。 缺点 ①占用服务器端资源。即服务器端完成HTML页面内容的拼接如果请求较多会对服务器造成一定的访问压力。 ②不利于前后端分离开发效率低。使用服务器端渲染则无法进行分工合作尤其对于前端复杂度高的项目不利于 项目高效开发。 前后端分离的Web开发模式 前后端分离的概念前后端分离的开发模式依赖于Ajax技术的广泛应用。简而言之前后端分离的Wb开发模式 就是后端只负责提供API接口前端使用AjaX调用接口的开发模式。 4.前后端分离的优缺点 优点 ①开发体验好。前端专注于U川页面的开发后端专注于pi的开发且前端有更多的选择性。 ②用户体验好。Ajx技术的广泛应用极大的提高了用户的体验可以轻松实现页面的局部刷新。 ③减轻了服务器端的渲染压力。因为页面最终是在每个用户的浏览器中生成的。 缺点 ①不利于SEO。因为完整的HTML页面需要在客户端动态拼接完成所以爬虫对无法爬取页面的有效信息。解决方 案利用Vue、React等前端框架的SSR(server side render)技术能够很好的解决SEO问题) 如何选择Web开发模式 不谈业务场景而盲目选择使用何种开发模式都是耍流氓。 比如企业级网站主要功能是展示而没有复杂的交互并且需要良好的SEO,则这时我们就需要使用服务器端渲染 而类似后台管理项目交互性比较强不需要考虑SEO,那么就可以使用前后端分离的开发模式。 另外具体使用何种开发模式并不是绝对的为了同时兼顾了首页的渲染速度和前后端分离的开发效率一些网站采用了 首屏服务器端渲染其他页面前后端分离的开发模式。 身份认证
什么是身份认证 身份认证(Authentication)又称身份验证”、鉴权”是指通过一定的手段完成对用户身份的确认。 日常生活中的身份认证随处可见例如高铁的验票乘车手机的密码或指纹解锁支付宝或微信的支付密码等。 在Web开发中也涉及到用户身份的认证例如各大网站的手机验证码登录、邮箱密码登录、二维码登录等。 为什么需要身份认证 身份认证的目的是为了确认当前所声称为某种身份的用户确实是所声称的用户。例如你去找快递员取快递你要怎么证明这份快递是你的。 在互联网项目开发中如何对用户的身份进行认证是一个值得深入探讨的问题。例如如何才能保证网站不会错误的将“马云的存款数额”显示到“马化腾的账户”上。 不同开发模式下的身份认证 对于服务端渲染和前后端分离这两种开发模式来说分别有着不同的身份认证方案 ①服务端渲染推荐使用Session认证机制 ②前后端分离推荐使用JWT认证机制 HTTP协议的无状态性 了解HTTP协议的无状态性是进一步学习Session认证机制的必要前提。 HTTP协议的无状态性指的是客户端的每次HTTP请求都是独立的连续多个请求之间没有直接的关系服务器不会主动保留每次HTTP请求的状态。 如何突破HTTP无状态的限制 对于超市来说为了方便收银员在进行结算时给VIP用户打折超市可以为每个VP用户发放会员卡。 辨别用户的VIP身份 注意现实生活中的会员卡身份认证方式在Web开发中的专业术语叫做Cookie。 了解Session认证的局限性 Session认证机制需要配合Cookie才能实现。由于Cookie默认不支持跨域访问所以当涉及到前端跨域请求后端接 口的时候需要做很多额外的配置才能实现跨域Session认证。 注意 ·当前端请求后端接口不存在跨域问题的时候推荐使用Session身份认证机制。 ·当前端需要跨域请求后端接口的时候不推荐使用Session身份认证机制推荐使用WT认证机制。 什么是Cookie Cookie是存储在用户浏览器中的一段不超过4KB的字符串。它由一个名称(Name)、一个值(Value)和其它几个用 于控制Cookie有效期、安全性、使用范围的可选属性组成。 不同域名下的Cookie各自独立每当客户端发起请求时会自动把当前域名下所有未过期的Cookie一同发送到服务器。 Cookiel的几大特性 自动发送 域名独立 过期时限 k4b限制 Cookie在身份认证中的作用 客户端第一次请求服务器的时候服务器通过响应头的形式向客户端发送一个身份认证的Cookie,客户端会自动 将Cookie保存在浏览器中。 随后当客户端浏览器每次请求服务器的时候浏览器会自动将身份认证相关的Cook,通过请求头的形式发送给 服务器服务器即可验明客户端的身份。 Cookie不具有安全性 由于Cookie是存储在浏览器中的而且浏览器也提供了读写Cookie的AP叫因此Cookie很容易被伪造不具有安全 性。因此不建议服务器将重要的隐私数据通过Cookie的形式发送给浏览器。 注意干万不要使用Cookie存储重要且隐私的数据比如用户的身份信息、密码等。 提高身份认证的安全性 为了防止客户伪造会员卡收银员在拿到客户出示的会员卡之后可以在收银机上进行刷卡认证。只有收银机确认存在的 会员卡才能被正常使用。 这种“会员卡刷卡认证”的设计理念就是Session认证机制的精髓。 nodejs 怎么连接mysql数据库
const express require(express) //引入express 模块
const app express() //创建实例
const mysql require(mysql) //引入mysql 模块
// 创建数据库连接 填入数据库信息
//填自己数据库的信息!!!!!!!!!!!
const conn mysql.createConnection({user:root, //用户名password:admin123, //密码host:localhost, //主机默认都是local hostdatabase:node //数据库名
})
// 测试连接
conn.connect(err{console.log(err,如果为null 就是连接成功);
})
// 开启服务器
app.listen(3100,(){console.log(服务器在3000端口开启。。。。。);
})//3.测试mysql模块能否正常工作
// 调用db.query0函数指定要执行的SQL语句通过回调函数拿到执行的结果
conn.query(select 1,(err,results){if (err)return console.log(88,err.message)//只要能打印出[RowDataPacket《·11}】的结果就证明数据库连接正常console.log(45,results)
})// 定义路由(说白了就是网址)
app.get(/add,(req,res){// 向数据库 student 表里增加 name age 数据let sqlStr INSERT INTO student ( name, age )VALUES(小刚,4345)//执行mysql 语句conn.query(sqlStr,(err){console.log(err,如果为nullsql语句执行成功)})//成功后的页面显示res.send(插入成功)
})// 查询信息
app.get(/find,(req,res){let sql SELECT * FROM student conn.query(sql,(err,results){//返回的查询信息为result 然后将其显示在页面上res.send(results)})
})// 更换用户头像
app.get(/update,(req,res){// 根据id 更新student 表里的 user_piclet sql update student set user_pic? where id?conn.query(sql,[req.body.avatar,req.body.id],(req,sqlRes) {// 执行 SQL 语句失败 if (err) return res.cc(err) // 执行 SQL 语句成功但是影响行数不等于 1 if (sqlRes.affectedRows ! 1) return res.cc(更新头像失败) // 更新用户头像成功 return res.cc(更新头像成功, 0)})
})// 删除数据
app.get(/delete/:id, (req, res) {let sql DELETE FROM student where id?conn.query(sql,[req.params.id], function (err, results) {if (err) return console.log(88, err.message)console.log(45, results)res.send({ mas: 删除成功, code: 200, data: results })})
})