Nodejs知识点总结及个人学习心得

本文详细介绍了Node.js的学习历程,包括环境安装、fs文件系统、path路径模块、http模块的web服务器创建、模块化、npm与包管理、Express框架的使用、接口编写、数据库操作、session和JWT认证机制。内容涵盖基础概念、常用模块及高级应用,适合Node.js初学者和进阶者阅读。
摘要由CSDN通过智能技术生成

Nodejs是一个基于Chrome V8引擎的JavaScript运行环境

node菜鸟文档

node中文文档

node千峰

七天学会Nodejs

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方模块加载机制

  1. 如果传递给require的不是一个内置模块,也没有以./,…/开头,则Node会从当前模块的父目录开始,尝试从/node_modules文件夹中加载第3方模块

  2. 如果没有找到对应的第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
    

目录(文件夹)作为模块

  1. 在被加载目录找一个package.json文件,并寻找main属性,作为require()加载的入口
  2. 如果目录没有package.json文件,或者main入口不存在或无法解析,则将会加载目录下的index.js文件
  3. 如果上面两部都失败了,则在终端打印错误消息

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)]

  1. 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方的中间件

  1. 应用级别的中间件

    通过app.use()或app.get()或app.post(),绑定到app实例上的中间件,
    
  2. 路由级别的中间件

    绑定到express.Router()实例上的中间件,用法与应用级别无区别,只不过一个是绑定到app实例上,一个是router实例上
    
  3. 错误级别的中间件

    • 错误中间件必须放在路由之后
    作用:专门用来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题
    格式:错误级别中间件的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');
    })
    
  4. 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}))
      
  5. 第3方中间件

    • 非express官方内置,需要大家按需下载并配置,,比如body-parser中间件,常用来解析请求体数据,使用步骤
      • 运行npm i body-parser安装中间件
      • 使用require导入中间件
      • 调用app.use()注册并使用中间件

自定义中间件

手动模拟一个类似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个响应头

  1. 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','*');
    
  2. 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);
    
  3. Access-Control-Allow-Methods

    默认情况下,CORS仅支持客户端发起GET,POSTHEAD请求
    如果客户端希望通过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,PUTDELETE等请求
// 必须在配置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)
---------------------------------------------------------
需要额外掌握的4SQL语法
where条件,and和or运算符,order by 排序, count(*)函数
                                     
 注释:--1个空格
  1. select语句,用于查询数据,结果存储在一个结果表中

    //从指定表中查询出所有的数据,*表示所有列
    select * from 表名称
    //从指定表中,查询出指定列名称(字段)的数据
    select 列名称 from 表名称
    //多个列之间用,分隔
    select 列名称1,列名称2 from 表名称
    
  2. 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')
    
  3. 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
    
  4. 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'

排序

  1. 升序

    order by 默认进行升序排序
    asc关键字代表升序排序,下面两条语句等价
    select * from users order by status
    seltct * from users order by status asc
    -------------------------------------------------------
    -- 对users表中的数据,按照status字段进行升序排序
    -- select * from users order by status
    
  2. 降序

    order by desc
    -- 对users表中的数据,按照id字段进行降序排序
    select * from users order by id desc
    
  3. 综合应用

    -- 先按照statu进行降序排序,再按照username字母的顺序进行升序排序
    select * from users order by status desc,username asc
    

count函数和as关键字

  1. count(*)函数用于返回查询结果的总数据条数

    select count(*) from 表名称
    -----------------------------------
    -- 使用count(*)来统计users表中,状态为0用户的总数量
    select count(*) from users where status=0
    
  2. as为列设置别名

    -- 使用as关键字给列起别名
    -- select count(*) as total from users where status=0
    select username as uname,password as upwd from users
    

在项目中操作mysql模块⭐️

  1. 安装mysql模块

    • npm i mysql 安装mysql模块
  2. 在使用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' //指定要操作哪个数据库
    })
    
  3. 测试mysql模块是否能正常工作

    // 测试mysql模块是否能正常工作
    db.query('select 1', (err, result) => {
      if (err) {
        console.log(err.message);
      } else {
        console.log(result);
      }
    })
    
  4. 查询数据

    //查询users表中所有的数据
    db.query('select * from users', (err, result) => {
      if (err) {
        console.log(err.message);
      } else {
        // 执行的是select查询语句,则执行的结构是数组
        console.log(result);
      }
    })
    
  5. 插入数据

    //向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('插入数据成功');
      }
    })
    
  6. 更新数据

    //更新用户信息
    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('更新数据成功');
      }
    })
    
  7. 删除数据,推荐根据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

  1. 安装JWT相关的包,npm i jsonwebtoken express-jwt

    • jsonwebtoken用于生成JWT字符串,express-jwt用于将JWT字符串解析还原成JSON对象
  2. 定义secret密匙

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tove5mr8-1668854115748)(D:\新的开始md\NodeJs\img\5.png)]

  3. 登录成功后生成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 字符串
        })
        })
      
  4. 将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)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值