Nodejs是一个基于Chrome V8引擎的JavaScript运行环境
1.Node.js环境的安装
两个版本;LTS和Current
LTS为长期稳定版,推荐安装
Current为新特性尝鲜版,可能存在隐藏bug
node -v 查看版本号
node运行js代码
- 方法1:window+r---->cmd,然后cd到文件目录在输入命令 node 1.js(文件名)
- 方法2:找到文件目录,按住shift+鼠标右击------->点击在此处打开powershall窗口
常用终端快捷键
方向键上箭头 | 快速定位到上一次执行的命令 |
---|---|
tab键 | 快速补全路径 |
esc键 | 快速清空当前已输入的命令 |
cls命令 | 可以清空终端 |
2.fs文件系统模块
- fs.readFile()方法,用来读取指定文件中的内容
- ==fs.writeFile()==方法,用来向指定的文件中写入内容
安装Node的时候自动把这些也安装了,如下方法导入它就好
const fs=require('fs')
fs.readFile()
fs.readFile(path[,options],callback)
- 参数1:必选参数,字符串,表示读取文件的路径
- 参数2:可选参数,表示以什么编码格式来读取文件
- 参数3:必选参数,文件读取完成后,通过回调函数拿到读取失败和成功的结果
- 如果读取成功,则err的值为null
如果读取失败,则err的值为错误对象,data的值为undefined
// 导入fs模块
const fs = require('fs');
fs.readFile('../file/r1.txt', 'utf-8', function (err, data) {
// 如果读取成功,则err的值为null
// 如果读取失败,则err的值为错误对象,data的值为undefined
console.log(err);
console.log('---------------');
console.log(data);
})
fs.writeFile()
fs.writeFile(file,data[,option],calback)
-
file:必选,文件存放路径
-
参数2data:必选,表示要写入的内容
-
参数3:可选,编码格式,默认utf-8
-
参数4:必选,回调函数
-
如果文件存在,该方法写入的内容会覆盖旧的文件内容。
-
// 写入文件内容 const fs = require('fs'); // 回调函数只包含错误信息参数(err) fs.writeFile('../file/2.txt', '我是写入的数据', function (err) { if (err) { console.log('文件导入失败' + err.message); } else { console.log('文件写入成功'); } })
路径动态拼接问题
-
PS D:\新的开始\NodeJs\day1> node .\code\1.js --------------------------------------------------- [Error: ENOENT: no such file or directory, open 'D:\新的开始\NodeJs\file\r1.txt']
-
我们代码读取的是这个文件fs.readFile(‘…/file/r1.txt’,,
它会直接用当前目录去拼接代码目录…/file/r1.txt,而不是拼接.\code\1.js,所以报错 -
解决方法
- __dirname表示当前文件所处的目录
3.path路径模块
path.join()
可以把多个路径片段拼接为完整的路径字符串 …/会抵消前一级的路径
以后拼接就用这个取代+,因为+容易出问题
const fs = require('fs');
const path = require('path');
// ../会抵消前一级的路径
const newPath = path.join('/a', './b/c', '../', './e');
console.log(newPath);//\a\b\e
// path会自动帮我们把.给屏蔽掉
// fs.readFile(__dirname, +'./5.');会报错,因为拼接的时候有.
fs.readFile(path.join(__dirname, './5.txt'), 'utf-8', function (err, data) {
if (err) {
console.log('路径错误' + err.message);
} else {
console.log(data);
}
})
path.basename()
获取路径中的文件名,可以获取路径中的最后一部分
path.basename()
获取路径的扩展名部分
const path = require('path');
const fpath = '/a/b/c/index.html';//文件的存放路径
var newFpath = path.basename(fpath);
console.log(newFpath);//index.html
console.log(path.basename(fpath, '.html'));//index
var newname = path.extname(fpath);
console.log(newname);//.html
4.http模块
Nodejs官方提供的,用来创建web服务器的模块,通过http模块提供的http.createServer()方法,就能把一台普通电脑,变成一台Web服务器,从而对外提供Web资源服务,通过IIS,Apach这些第3方服务器软件也行,但是nodez中不需要使用这些
- 开发期间,自己电脑既是服务器也是客户端,为了方便测试,可以在自己浏览器中输入127.0.0.1这个IP地址,就可以把自己电脑当做一台服务器进行访问了
- 127.0.0.1对应的域名是localhost
- 端口号
- 每个端口号不能同时被多个web服务占用
- URl中的80端口可以被省略
创建最基本的web服务器
// 导入http
const http = require('http');
// 2.创建web服务器实例
const server = http.createServer();
//3. 为服务器实施绑定request事件,监听客户端的请求
server.on('request', function (req, res) {
console.log('Someone visit our web server');
})
//4.启动服务器
server.listen(8080, function () {
console.log('server running at http://127.0.0.1:8080');
})
/*
server.listen(80, function () {
// 监听的是80端口,则可以省略不写
console.log('server running at http://127.0.0.1');
})
*/
req请求对象
req是请求对象,包含了与客户端相关的数据和属性
这里服务器没有向客户端res响应内容默认失败
const http = require('http');
const server = http.createServer();
server.on('request', function (req, res) {
var url = req.url;
var method = req.method;
const str = `你请求的路径是 ${url},请求的方法为 ${method}`;
console.log(str);
})
server.listen(80, () => {
console.log('server running at http://127.0.0.1');
})
res响应对象及解决响应中文乱码问题
req.url得到的是127.0.0.1后面的内容
const http = require('http');
const server = http.createServer();
server.on('request', function (req, res) {
var url = req.url;
var method = req.method;
const str = `你请求的路径是 ${url},请求的方法为 ${method}`;
console.log(str);
// 使用res.end方法向客户端发送中文,会出现乱码,需要手动设置内容的编码格式
res.setHeader('Content-Type', 'text/html; charset=utf-8')
// 调用res.end()方法,向客户端响应一些内容
res.end(str);
})
server.listen(80, () => {
console.log('server running at http://127.0.0.1');
})
5.Nodejd中的模块化
- 加载模块:require()方法,可以加载需要的内置模块,用户自定义,第3方模块
- 使用require()方法加载其他模块时,会执行被加载模块中的代码
- 使用require导入自定义模块的时候可以省略后缀名
1.js
console.log('我是被加载的模块');//执行2.js,1.js的代码也会被执行
------------------------------------
2.js
const m = require('./1.js');
console.log(m);//{}
模块作用域
- 默认情况下,node模块有自己的作用域,在模块定义的同学无法被外界访问
- 防止了全局变量污染的问题
module对象😆
-
在自定义模块中,可以使用module.exports对象,将模块内的成员共享出去
-
外界用require()方法导入自定义模块时,得到的就是module.exports所指的对象,而module.export默认为空对象,所以上面代码输出的m是{}
-
使用require方法导入模块时,导入的结果永远以module.exports指向对象为准!
const age = 20; module.exports.usersname = '刘总'; module.exports.sayHello = function () { console.log('hellow'); } module.exports.age = age; // 使用require方法导入模块时,导入的结果永远以module.exports指向对象为准! module.exports = { usersname: '小黑', sayHi() { console.log('hi'); } } ----------------------------------------------------- const m = require('./module对象'); console.log(m);//{ usersname: '小黑', sayHi: [Function: sayHi] }
exports对象
Node提供得的exports对象,默认情况下,与module.exports指向同一个对象,但最终结果,还是以module.exports指向的对象为准
- 为了防止混乱,建议大家不要在同一个模块中同时使用exports和module.exports
Nodejs中的模块化规范⭐️
Node遵循了CommonJs模块化规范,CommonJS规定了模块的特性和各模块之前的相互依赖
- 每个模块内部,module变量代表当前模块
- module变量是一个对象,它的exports属性(即module.exports)是对外的接口
- 加载某个模块,其实是加载该模块的module.exports属性
- require()方法用来加载模块
6.npm与包
npm指令
npm i 包名 包名 安装多个包,空格隔开
npm i 包名@2.24.0 安装指定包
npm init -y 创建package.json包管理配置文件,项目文件夹不能使用中文和空格
npm uninstall 包名 卸载包
npm i 包名 -D===npm i 包名 --save-dev ,把包安装到devDependencies节点中
- 初次装包后,多了node_modules文件夹和packagge-lock.json配置文件
- node_modules用来存放已安装到项目中的包,require()导入包就是从这个目录查找并加载
- package-lock-json用来记录每个包的下载信息
nodeJs安装的时候已经把下载包的一起安装了,npm -v就可以查看npm包管理工具的版本号
//npm i moment
const moment = require('moment');
const dt = moment().format('YYYY-MM--DD HH:mm:ss');
console.log(dt);
包管理配置文件
package.json------------npm init -y
在项目根目录,必须提供package.json的包管理配置文件,用来记录与项目有关的配置信息;
项目开发中,要把node_modules文件夹,添加到.gitignore忽略文件中
项目开发阶段用,上线不用,则把这些包记录devDependencies节点中
开发和项目上线后都需要用到,则记录到dependencies节点中
- dependencies节点:记录npm install命令装了哪些包,
- 如果不小心删了node_modules,执行会报错,我们要npm i 一次性安装回package.json的所有包
- npm uninstall 包名卸载的包,会自动从package.json的dependencies中移除掉
- devDependencies节点:记录 npm i 包名 -D安装的包
包下载速度慢
-
因为npm下包的时候,默认从国外服务器下载,所以速度慢
-
解法:淘宝镜像:淘宝在国内搭建的一个服务器
-
正常切换npm包镜像源
//查看当前的下包镜像源 npm config get registry //将下包的镜像源切换为淘宝镜像 npm config set registry=https://registry.npm.taobao.org/
nrm
-
这个工具可以更方便的切换下包的镜像源
-
//安装 npm i nrm -g //查看所有可用的镜像源 nrm ls //将下包的镜像源切换为taobao镜像 nrm use taobao
包的分类和发布
-
全局包
-
npm i 包名 -g 全局安装指定的包 npm uninstall 包名 -g 卸载全局安装的包
-
只有工具性质的包,才有全局安装的必要,
-
判断是否需要全局安装才能使用,可以参考官方文档说明
-
-
发布包
-
1.访问https://www.npmjs.com/网站,点击sign up按钮,进入注册用户界面 2.填写账号相关的信息:Full Name,Public Email,Username,Password 3.点击Create an Account按钮,注册账号 4.登录邮箱,点击验证链接,进行账号的验证 5.账号注册完成后,在终端执行npm login命令,必须先把下包的服务器地址切换为npm官方服务器 6.切换到包的根目录,运行npm publish命令,发布完成 7.删除已发布的包 npm unpublish 包名 --force命令
-
7.模块加载机制
优先从缓存中加载
- 模块在第一次加载后会被缓存,后面执行都会优先从缓存中加载,从而提高加载效率
内置模块的加载优先级最高
- require(‘fs’)始终返回内置fs模块,即使在node_modules目录下有名字相同的的包叫做js;
自定义模块加载机制
- 使用require()加载自定义模块时,必须指定以./或…/开头的路径标识符。
- 加载自定义模块时,如果没有指定这样的路径标识符,则node会把它当作内置模块或第3方模块进行加载
- require()导入自定义模块时,如果省略了扩展名,则node会按顺序尝试加载以下文件
- 按照确切的文件名进行加载
- 补全.js扩展名进行加载
- 补全.json扩展名进行加载
- 补全node扩展名进行加载
- 加载失败,终端报错
第3方模块加载机制
-
如果传递给require的不是一个内置模块,也没有以./,…/开头,则Node会从当前模块的父目录开始,尝试从/node_modules文件夹中加载第3方模块
-
如果没有找到对应的第3方模块,则移动到再上一层父目录中,进行加载,直到根目录
-
假设在C\Users\itheima\project\foo.js文件里调用了require(‘tools’),则node会按以下顺序查找
C\Users\itheima\project\node_modules\tools C\Users\itheima\node_modules\tools C\Users\node_modules\tools C\node_modules\tools
目录(文件夹)作为模块
- 在被加载目录找一个package.json文件,并寻找main属性,作为require()加载的入口
- 如果目录没有package.json文件,或者main入口不存在或无法解析,则将会加载目录下的index.js文件
- 如果上面两部都失败了,则在终端打印错误消息
8.Express@4.17.1
http内置模块开发效率低,Express基本内置的http模块进一步封装出来的,极大提高开发效率
Vue cli里面自带这个
初体验
//导入express
const express = require('express');
//创建web服务器
const app = express();
//启动web服务器
app.listen(80, () => {
console.log('express server running at http://127.0.0.1');
})
get和post
//app.get('请求url',function(req,res){/*处理函数*/})
app.post('请求url',function(req,res){/*处理函数*/})
参数1:客户端请求的url地址
参数2:请求对应的处理函数
req:请求对象(包含了与请求相关的属性与方法)
res:响应对象(包含了与响应相关的属性与方法)
-
res.send()方法,可以把处理好的内容,发给客户端
-
req.query,可以访问到客户端通过查询字符串的形式,发送到服务器的参数:get请求
-
req.body则是则拿到的是是客户端data里面的数据:针对post请求
req.query默认情况下是一个空对象 例如客户端使用?name=zs&age=20 这种查询字符串形式,发送到服务器的参数 可以通过req.query.name req.query.age
-
req.params,可以访问到url中,通过:匹配到的动态参数:默认情况下是一个空对象
app.get('/user/:id', function (req, res) { console.log(req.params); res.send(req.params); })
托管静态资源
express.static(),可以便捷创建一个静态资源服务器,app.use()函数作用,就是来注册全局中间件
app.use(express.static('public'))
现在就可以访问public目标中的所有文件了:
http://localhost:3000/image.bg.jpg
http://localhost:3000/css/style.cc
express在指定静态目录中查找文件,并对外提供资源访问路径,因此,存放静态文件的目录名不会出现在URL中
const express = require('express');
const app = express();
app.use(express.static('./时钟案例'))
app.listen('80', () => {
console.log('express server running at http://127.0.0.1');
})
托管多个静态资源目录
app.use(express.static('public'))
app.use(express.static('files'))
-
访问静态资源文件时,express.static()函数会根据目录的添加顺序查找所需文件
-
会默认找文件夹里面的index.html文件
-
const express = require('express'); const app = express(); // 按先后顺序 //app.use(express.static('./时钟案例')); app.use(express.static('./file')); app.use(express.static('./时钟案例')); app.listen('80', () => { console.log('express server running at http://127.0.0.1'); })
挂载路径前缀
app.use('/public',express.static('public'));
现在就可以通过带有/public前缀地址来访问public目标中的所有文件了:
http://localhost:3000/public/image.bg.jpg
http://localhost:3000/public/css/style.cc
const express = require('express');
const app = express();
// 按先后顺序
app.use('/时钟案例', express.static('./时钟案例'));
app.use(express.static('./file'));
app.listen('80', () => {
console.log('express server running at http://127.0.0.1');
})
如果http://127.0.0.1/会访问.file下面的,本来按顺序应该是时钟的,但他前面得有./时钟案例前缀,得http://127.0.0.1/时钟案例才能访问到
Express中的路由
路由由3部分组成,请求的类型,请求的URL地址,处理函数
app.METHOD(PATH,HANDER)
--------------------------------
基本使用
const express = require('express');
const app = express();
app.get('/', function (req, res) {
res.send('我是get方法');
})
app.post('/', (req, res) => {
res.send('我是post方法')
})
app.listen(80, () => {
console.log('http://127.0.0.1');
})
模块化路由
-
为了方便对路由进行模块化的管理,express不建议将路由直接挂载到app上,而是推荐将路由抽离为单独模块
-
步骤如下
-
1.创建路由模块对应的js文件 2.调用express.Router()函数创建路由对象 3.向路由对象上挂载具体的路由 4.使用module.exports向外共享路由对象 5.使用app.use()函数注册路由模块
-
// 1.导入express const express = require('express'); // 2.创建路由对象 const router = express.Router(); // 3.挂载具体的路由 router.get('/user/list', (req, res) => { res.send('Get user list.') }) router.post('/user/add', (req, res) => { res.send('Post new user .') }) // 向外导出路由对象 module.exports = router;
-
const express = require('express'); const app = express(); // 1。导入路由模块 const router = require('./7.router'); // 2.注册路由模块 app.use(router) app.listen(80, () => { console.log('http://127.0.0.1'); })
-
为路由模块添加前缀
和上面的3.2挂载路径前缀一样
中间件
一般注册在路由之前,错误级别中间件除外
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-axcg13KG-1668854115743)(D:\新的开始md\NodeJs\img\1.png)]
-
next函数的作用
- 实现多个中间件连续调用的关键,它表示把流转关系转交给下一个中间件或路由
**全局生效的中间件
客户端发送的任何请求,到达服务器后都会触发的中间件,调用app.use(中间件函数),即可以定义
const express = require('express');
const app = express();
/*
// 定义一个最简单的中间件函数
const mw = function (req, res, next) {
console.log('必须包含next参数');
// 把流转关系,转交给下一个中间件或路由
next();
}
// 将mw注册为全局生效的中间件
app.use(mw);
*/
app.use(function (req, res, next) {
console.log('必须包含next参数');
// 把流转关系,转交给下一个中间件或路由
next();
})
app.get('/', function (req, res) {
console.log('我是/路由');
res.send('我是/')
})
app.post('/user', function (req, res) {
console.log('我是/user路由');
res.send('我是user')
})
app.listen(80, () => {
console.log('http://127.0.0.1');
})
中间件的作用⭐️
- 中间件的作用:多个中间件之间共享同一份req和res,所以我们可以在上游的中间件中,统一为req或res对象添加自定义的属性或方法,供下游的中间件或路由进行使用
const express = require('express');
const app = express();
app.use(function (req, res, next) {
console.log('必须包含next参数');
const time = Date.now();
// 为req对象挂载自定义属性,从而把时间共享给后面的所有路由
req.starTime = time;
// 把流转关系,转交给下一个中间件或路由
next();
})
app.get('/', function (req, res) {
console.log('我是/路由');
res.send('我是/' + req.starTime);
})
app.post('/user', function (req, res) {
console.log('我是/user路由');
res.send('我是user' + req.starTime)
})
app.listen(80, () => {
console.log('http://127.0.0.1');
})
定义多个全局中间件
使用app.use()连续定义多个全局中间件,客户端请求到达服务器后,会按照中间件定义的先后顺序依次进行调用
app.use(function (req, res, next) {
console.log('我是第一个中间件');
next()
})
app.use(function (req, res, next) {
console.log('我是第2个中间件');
next()
})
局部生效的中间件
不使用app.use()定义的中间件,叫做局部生效的中间件
const express = require('express');
const app = express();
const ab = function (req, res, next) {
console.log('我是局部中间件');
next();
}
app.get('', ab, function (req, res) {
console.log('局部中间件的使用');
res.send('局部')
})
app.get('/user', function (req, res) {
console.log('不会调用局部中间件');
res.send('局部')
})
app.listen(80, () => {
console.log('http://127.0.0.1');
})
定义多个局部中间件
方法1:app.get('', ab1,ab2, function (req, res) {}
方法2 app.get('',[ab1,ab2], function (req, res) {}
中间件使用注意事项
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a5YqjQYn-1668854115745)(D:\新的开始md\NodeJs\img\2.png)]
中间件的分类⭐️
5类,应用,路由,错误级别的中间件,Express内置的中间件,第3方的中间件
-
应用级别的中间件
通过app.use()或app.get()或app.post(),绑定到app实例上的中间件,
-
路由级别的中间件
绑定到express.Router()实例上的中间件,用法与应用级别无区别,只不过一个是绑定到app实例上,一个是router实例上
-
错误级别的中间件
- 错误中间件必须放在路由之后
作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题 格式:错误级别中间件的function处理函数中,必须有4个形参,形参顺序从前到后,分别是(err,req,res,next) const express = require('express'); const app = express(); app.get('/', function (req, res) { // 人为制造错误 throw new Error('服务器内部发生了错误!'); }) // 正常会程序崩溃,所以我们搞个错误中间件捕获错误 app.use((err, req, res, next) => { console.log('发生了错误' + err.message);//在服务器打印错误信息 res.send('Error' + err.message);//向客户端响应错误相关的内容 }) app.listen(80, () => { console.log('express server running at http://127.0.0.1'); })
-
Express内置的中间件
-
express.static:快速托管静态资源的内置中间件
-
express.json:解析JSON格式的请求体数据
-
express.urlencoded:解析URL-encoded格式的请求体数据
//配置解析application/json格式数据的内置中间件 app.use(express.json()) 可以使用req.body来接收客户端发送过来的请求体数据 默认情况下,如果不配置解析表单数据的中间件,则req.body默认等于undefined ------------------------------ //配置解析application/x-www-form-urlencoded 格式数据的内置中间件 app.use(express.urlencoded({extended:false}))
-
-
第3方中间件
- 非express官方内置,需要大家按需下载并配置,,比如body-parser中间件,常用来解析请求体数据,使用步骤
- 运行npm i body-parser安装中间件
- 使用require导入中间件
- 调用app.use()注册并使用中间件
- 非express官方内置,需要大家按需下载并配置,,比如body-parser中间件,常用来解析请求体数据,使用步骤
自定义中间件
手动模拟一个类似express.urlencoded这样的中间件,来解析Post提交到服务器的表单数据,实现步骤如下:
1.定义中间件 2,监听req的data事件 3.监听req的end事件 4.使用querystring模块解析请求体数据
5,将解析出来的数据对象挂载为req.body 6,将自定义中间件封装为模块
9.接口编写
get接口
const express = require('express');
const app = express();
//配置解析表单数据的中间件,不然req.body会返回Undefined
app.use(express.urlencoded({ extended: false }))
const router = require('./2.router');
// 把路由模块注册到app上
app.use('/api', router);
app.listen(80, () => {
console.log('runing http://127.0.0.1');
})
--------------------------------------
const { query } = require('express');
const express = require('express');
const router = express.Router();
router.get('/get', function (req, res) {
const query = req.query;
res.send({
status: 0,
msg: 'GET请求成功',
data: query
})
})
module.exports = router;
post接口
router.post('/post', function (req, res) {
const body = req.body;
res.send({
status: 0,
msg: 'POST请求成功',
data: body
})
})
基于cors解决端口跨域问题⭐️
-
cors是express的一个第3方中间件,通过安装和配置cors中间件,跨域很方便的解决跨域问题
- npm i cors 安装中间件
- const cors=require(‘cors’) 导入中间件
- 在路由之前调用app.use(cors()) 配置中间件
-
CORS主要在服务器端进行配置,客户端浏览器无须做任何额外的配置
-
const express = require('express'); const app = express(); //配置解析表单数据的中间件,不然req.body会返回Undefined app.use(express.urlencoded({ extended: false })); const cors = require('cors'); app.use(cors()); const router = require('./2.router'); // 把路由模块注册到app上 app.use('/api', router); app.listen(80, () => { console.log('runing http://127.0.0.1'); }) ------------------------------------------------------------------- <button id="btnGET">GET</button> <button id="btnPOST">POST</button> <script> $('#btnGET').on('click', function () { $.ajax({ type: 'GET', url: 'http://127.0.0.1/api/get', data: { name: 'zs', age: 20 }, success: function (res) { console.log(res); } }) }) $('#btnPOST').on('click', function () { $.ajax({ type: 'POST', url: 'http://127.0.0.1/api/post', data: { bookname: '水浒传', author: '施耐庵' }, success: function (res) { console.log(res); } }) }) </script>
-
CORS是由一系列HTTP响应头组成,这些响应头决定浏览器是否阻止前端js代码跨域,默认阻止,可以配置CORS相关响应头解除跨域限制
core相关的3个响应头
-
Access-Control-Allow-Origin
Access-Control-Allow-Origin:<origin>|*,orgin参数的1值指定允许访问该资源的外域URL 举例下面的字段只允许来自http://itcast.cn res.setHeader('Access-Control-Allow-Origin','http://itcast.cn'); //表示允许来自任何域的请求 res.setHeader('Access-Control-Allow-Origin','*');
-
Access-Control-Allow-Headers
默认情况下,CORS仅支持客户端向服务器发送如下9个请求头: 如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过Access-Control-Allow-Headers对额外的请求头进行声明,否则这次请求会失败 //比如允许客户端向服务器发送Content-Type,请求头和X-Custom-Header的请求头 res.setHeader('Access-Control-Allow-Headers','Content-Type',X-Custom-Header);
-
Access-Control-Allow-Methods
默认情况下,CORS仅支持客户端发起GET,POST,HEAD请求 如果客户端希望通过PUT,DELETE等方式请求服务器的资源,则需要在服务器端,通过Access-Control-Allow-Methods来指明实际请求所允许使用的HTTP方法 //只允许POST,GET,DELETE,HEAD请求方法 res.setHeader('Access-Control-Allow-Methods','POST,GET,DELETE,HEAD'); //允许所有的HTTP请求方法 res.setHeader('Access-Control-Allow-Methods','*');
编写jsonp接口
JSONP:浏览器通过<script>标签的src属性,请求服务器上的数据,同时服务器返回一个函数的调用
特点:仅支持GET请求,不支持POST,PUT,DELETE等请求
// 必须在配置CORS之前声明JSONP的接口
app.get('/api/jsonp', (req, res) => {})
app.use(cors());
- 实现jsonp接口的步骤
- 获取客户端发送过来的回调函数的名字
- 得到要通过JSONP形式发送给客户端的数据
- 根据前两步得到的数据,拼接出一个函数调用的字符串
- 把上一步拼接得到的字符串,响应给客户端的标签进行解析执行
10.数据库
Mysql Workbench管理数据库
- DataType数据类型
- int整数
- varchar(len)字符串
- tinyint(1)布尔值
- 字段的特殊标识
- PK:主键,唯一标识
- NN: 值不允许为空
- UQ:值唯一
- AI:值自动增长
- 获取用get,删除用delete,添加用post,修改用put方法
SQL
-
结构化查询语言,是一门数据库编程语言
-
使用SQL语言编写出来的代码,叫做SQL语句
-
SQL语言只能在关系型数据库中使用,非关系型数据库(Mongodb)
-
输入新命令之前要把上一句命令注释掉,点击左上角第一个工具可以输入查询语句
-
左上角工具栏第2个可以打开我们的mysql文件
-
//1.导入数据库模块 const mysql = require('mysql'); //2.建立于Mysql数据库的连接关系 const db = mysql.createPool({ host: '127.0.0.1', //数据库的Ip地址 user: 'root', //登录数据库的账号 password: 'admin123', //'登录数据库的密码' database: 'my_db_01' //指定要操作哪个数据库 }) /* // 测试mysql模块是否能正常工作 db.query('select 1', (err, result) => { if (err) { console.log(err.message); } else { console.log(result); } }) */ /*
-
-
注意:列和值要一一对应,多个列和多个值之间,使用英文的逗号分隔
查询数据(select),插入数据(insert into),更新数据(update),删除数据(delete)
---------------------------------------------------------
需要额外掌握的4种SQL语法
where条件,and和or运算符,order by 排序, count(*)函数
注释:--加1个空格
-
select语句,用于查询数据,结果存储在一个结果表中
//从指定表中查询出所有的数据,*表示所有列 select * from 表名称 //从指定表中,查询出指定列名称(字段)的数据 select 列名称 from 表名称 //多个列之间用,分隔 select 列名称1,列名称2 from 表名称
-
insert into语句用于向数据表中插入新的数据行
语法解读:向指定表中,插入如下几列数据,列的值通过values一一指定 注意:列和值要一一对应,多个列和多个值之间,使用英文的逗号分隔 insert into table_name (列1,列2....) values (值1,值2...) ----------------------------------------------------------- 举例 -- 向users表中,插入新数据,username的值为tonystark,password的值为098123 -- insert into users (username,password) value ('tonystark','098123')
-
Update语句用于修改表中的数据
用update指定要更新哪个表中的数据 用set指定对应的新值 用where指定更新的条件 update 表名称 set 列名称 = 新值 where 列名称 = 某值 ------------------------------------------------------- -- 将密码为4的用户密码,更新成888888 update users set password = 888888 where id = 4 -- 更新id为2的用户,把用户密码更新为admin123 同时把用户状态更新为1 update users set password = 'admin123',status = 1 where id = 2
-
delete删除语句
删除这张表所有数据 delete form user 从users表中,删除id为4的用户 delete form user where id=4
SOL的where字句,and和or运算符
where字句用于限定选择的标准
操作符 描述 <>,有些版本可以写完!= 不等于 between 在某个范围内 like 搜索某种模式 = 等于
where
-- 查询id>2的
-- select * from users where id>2
and
-- 显示所有状态为0且id小于3的用户alter
select * from users where id<3 and status=0
-- 使用or来显示所有状态为1或username为zs的用户
select * from users where status=1 or username='zs'
排序
-
升序
order by 默认进行升序排序 asc关键字代表升序排序,下面两条语句等价 select * from users order by status seltct * from users order by status asc ------------------------------------------------------- -- 对users表中的数据,按照status字段进行升序排序 -- select * from users order by status
-
降序
order by desc -- 对users表中的数据,按照id字段进行降序排序 select * from users order by id desc
-
综合应用
-- 先按照statu进行降序排序,再按照username字母的顺序进行升序排序 select * from users order by status desc,username asc
count函数和as关键字
-
count(*)函数用于返回查询结果的总数据条数
select count(*) from 表名称 ----------------------------------- -- 使用count(*)来统计users表中,状态为0用户的总数量 select count(*) from users where status=0
-
as为列设置别名
-- 使用as关键字给列起别名 -- select count(*) as total from users where status=0 select username as uname,password as upwd from users
在项目中操作mysql模块⭐️
-
安装mysql模块
- npm i mysql 安装mysql模块
-
在使用msql操作数据库之前,必须先对mysql模块进行必要的配置
//1.导入数据库模块 const mysql = require('mysql'); //2.建立于Mysql数据库的连接关系 const db = mysql.createPool({ host: '127.0.0.1', //数据库的Ip地址 user: 'root', //登录数据库的账号 password: 'admin123', //'登录数据库的密码' database: 'my_db_01' //指定要操作哪个数据库 })
-
测试mysql模块是否能正常工作
// 测试mysql模块是否能正常工作 db.query('select 1', (err, result) => { if (err) { console.log(err.message); } else { console.log(result); } })
-
查询数据
//查询users表中所有的数据 db.query('select * from users', (err, result) => { if (err) { console.log(err.message); } else { // 执行的是select查询语句,则执行的结构是数组 console.log(result); } })
-
插入数据
//向users表中,新增一条数据,其中username的值为Spider-Man,password的值为pcc123 const user = { username: 'Spider-Man', password: 'pcc123' }; const sqlStr = 'insert into users (username,password) values (?,?)'; //使用数组的形式,依次为?占位符指定具体的值 db.query(sqlStr, [user.username, user.password], (err, data) => { if (err) { console.log(err.message); } else if (data.affectedRows === 1) { console.log('插入数据成功'); } }) ------------------------------------------------------------------------ 插入数据的便捷方式 const user = { username: 'Spider-Man2', password: 'pcc4321' }; const sqlStr = 'insert into users set ?'; //这里的data是一个对象 db.query(sqlStr, user, (err, data) => { if (err) { console.log(err.message); } else if (data.affectedRows === 1) { console.log('插入数据成功'); } })
-
更新数据
//更新用户信息 const user = { id: 888893, username: 'aaa', password: '000' }; //定义SQL语句 const sqlStr = 'update users set username=?,password=? where id=?' db.query(sqlStr, [user.username, user.password, user.id], (err, result) => { if (err) { console.log(err.message); } else if (result.affectedRows === 1) { console.log('更新数据成功'); } }) */ //更新用户信息,快捷方法 const user = { id: 6, username: 'aaa', password: '0000' }; //定义SQL语句 const sqlStr = 'update users set ? where username=?'; db.query(sqlStr, [user, user.username], (err, data) => { if (err) { console.log(err.message); } else if (data.affectedRows === 1) { console.log('更新数据成功'); } })
-
删除数据,推荐根据id这样的唯一标识来删除对应的数据
/*删除数据,如果sql语句有多个占位符,则必须使用数组为某个占位符指定具体的值 如果只有一个占位符,则可以省略数组,删除id为4的用户 */ const sqlStr = 'delete from users where id=?'; //这里的data是一个对象 db.query(sqlStr, 4, (err, data) => { if (err) { console.log(err.message); } else if (data.affectedRows === 1) { console.log('删除数据成功'); } })
11.session认证机制
http协议的无状态性:服务器不会主动保留每次http请求的状态,顾客办了会员卡买东西就是发请求,而服务员是不会记住顾客身份的,下次去不带卡一样没优惠,对于超市来说,为了方便收银员在进行结算时给VIP用户打折,超市可以为每个VIP用户发放会员卡而会员卡身份认证方式,就是cookie
cookie是存储在用户浏览器中的一段不超过4kb的字符串,由一个名称,一个值和其他几个用于控制cookie有效期,安全性,使用范围的可选属性组成
Cookie特性-------1.自动发送 2.域名独立 3.过期时限 4.4KB限制
Cookie不具有安全性,存储在浏览器中,很容易被伪造,所以千万不要使用Cookie存储重要且隐私的数据!
为了提高身份认证安全性,拿到会员卡后还得在收银机进行刷卡证,这就是Session认证机制的精髓
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JQsQ5sez-1668854115746)(D:\新的开始md\NodeJs\img\3.png)]
在Express中使用Session认证
安装配置express-session中间件
const session = require('express-session')
app.use(
session({
secret: 'itheima',
resave: false,
saveUninitialized: true,
})
)
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// TODO_01:请配置 Session 中间件
const session = require('express-session')
app.use(
session({
secret: 'itheima',
resave: false,
saveUninitialized: true,
})
)
// 托管静态页面
app.use(express.static('./pages'))
// 解析 POST 提交过来的表单数据
app.use(express.urlencoded({ extended: false }))
// 登录的 API 接口
app.post('/api/login', (req, res) => {
// 判断用户提交的登录信息是否正确
if (req.body.username !== 'admin' || req.body.password !== '000000') {
return res.send({ status: 1, msg: '登录失败' })
}
// TODO_02:请将登录成功后的用户信息,保存到 Session 中
// 注意:只有成功配置了 express-session 这个中间件之后,才能够通过 req 点出来 session 这个属性
req.session.user = req.body // 用户的信息
req.session.islogin = true // 用户的登录状态
res.send({ status: 0, msg: '登录成功' })
})
// 获取用户姓名的接口
app.get('/api/username', (req, res) => {
// TODO_03:请从 Session 中获取用户的名称,响应给客户端
if (!req.session.islogin) {
return res.send({ status: 1, msg: 'fail' })
}
res.send({
status: 0,
msg: 'success',
username: req.session.user.username,
})
})
// 退出登录的接口
app.post('/api/logout', (req, res) => {
// TODO_04:清空 Session 信息
req.session.destroy()
res.send({
status: 0,
msg: '退出登录成功',
})
})
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(80, function () {
console.log('Express server running at http://127.0.0.1:80')
})
12.JWT认证机制
选型
- 当前端请求后端接口不存在跨域问题的时候,推荐使用Session身份认证机制
- 需要跨域,则推荐使用JWT认证机制
- [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2KOnt8ht-1668854115747)(D:\新的开始md\NodeJs\img\4.png)]
JWT的组成部分
-
JWT通常由3部分组成,Header(头部),Payload(有效荷载),Signature(签名)
-
三者之间用英文的.分隔,如下
Header.Payload.Signature
Payload部分才是真正的用户信息,加密后生成的字符串
使用方式
-
把JWT放在HTTP请求头的Authorization字段中,格式如下
Authorizaton:Bearer <token>
在Expres中使用JWT
-
安装JWT相关的包,npm i jsonwebtoken express-jwt
- jsonwebtoken用于生成JWT字符串,express-jwt用于将JWT字符串解析还原成JSON对象
-
定义secret密匙
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tove5mr8-1668854115748)(D:\新的开始md\NodeJs\img\5.png)]
-
登录成功后生成jwt字符串
-
调用jsonwebtoken包提供的sign()方法,将用户的信息加密成JWT字符串,响应给客户端
-
// TODO_03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端 //参数1:用户信息对象,参数2:加密的密匙,参数3:配置对象,可以配置当前token的有效期 const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' }) res.send({ status: 200, message: '登录成功!', token: 'tokenStr' // 要发送给客户端的 token 字符串 }) })
-
-
将JWT字符串还原为JSON对象
- 客户端每次在访问那些有权限接口的时候,都需要主动通过请求头中的Authorization字段,将Token字符串送到服务器进行身份认证,此时服务器可以通过express-jwt这个中间件,自动将客户端发送过来的Token解析成JSON对象
// TODO_04:注册将 JWT 字符串解析还原成 JSON 对象的中间件 //unless指定哪些方法不需要权限 app.use(expressJWT({ secret: secretKey }).unless({path:[/^\/api\//]}))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lnzzR0pQ-1668854115749)(D:\新的开始md\NodeJs\img\6.png)]