node笔记 | 青训营笔记

117 阅读16分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 7 天

React 16.8+的生命周期分为三个阶段,分别是挂载阶段、更新阶段、卸载阶段。

fs模块

读文件

   <script>
        const fs = require('fs')
        fs.readFile('./node1.txt', 'utf8', function(err, dataStr) {
           if(err){
               return console.log('读取文件失败'+err.message)
           }
            console.log('成功'+dataStr)
        })
    </script>

写文件

  <script>
        const fs = require('fs')
        fs.writeFile('./node1.txt', '内容', function(err, dataStr) {
           if(err){
               return console.log('读取文件失败'+err.message)
           }
            console.log('成功'+dataStr)
        })
    </script>
  • _ _dirname表示当前文件的目录
  • 处理路径问题的时候 一定要用__dirname拼接地址
  • fs.writeFile(__dirname'./node1.txt', '内容', function(err, dataStr) {
     
    

path模块

  • 是node官方提供的 用来处理路径的模块,它提供了一系列的方法和属性 用来满足用户对路径的处理需求
//path.join()用来将多个路径片段拼接成一个完整的路径字符串
//path.basename()方法,用来从路径字符串中  讲文件名解析出来
//path.extname()可以获取路径中的拓展名部分
  • 先导入
  • const path = requir('path')
    

时钟案例

const fs = require('fs')
const path = require('path')
const regstyle = /<style>[\s\S]*</style>/
const regscript = /<script>[\s\S]*</script>/
fs.readFile(path.join(__dirname, './时钟案例.html'), 'utf8', function(err, dataStr) {
    if (err) return console.log('读取失败' + err.message)
        //读取成功后 分别调对应的三个方法,拆出css html js
    resolveCSS(dataStr)
    resolveJS(dataStr)
    resolvehtml(dataStr)
})
​
function resolveCSS(htmlStr) {
    const r1 = regstyle.exec(htmlStr)
    const newcss = r1[0].replace('<style>', '').replace('</style>', '')
    fs.writeFile(path.join(__dirname, './时钟案例.css'), newcss, function(err) {
        if (err) return console.log('写入css样式失败!' + err.message)
        console.log('写入样式文件成功!')
    })
}
​
function resolveJS(htmlStr) {
    const r2 = regscript.exec(htmlStr)
    const newjs = r2[0].replace('<script>', '').replace('</script>', '')
    fs.writeFile(path.join(__dirname, './时钟案例.js'), newjs, function(err) {
        if (err) return console.log('写入js样式失败!' + err.message)
        console.log('写入样式文件成功!')
    })
}
​
function resolvehtml(htmlStr) {
    htmlStr.replace(regstyle, '<link>')
}

http模块

  1. 导入http模块
  2. 创建web服务器实例
  3. 为服务器实例绑定request事件,监听客户端的请求
  4. 启动服务器
const http = require('http')
    //创建wub服务器实例
const server = http.createServer()
    //为服务器实例绑定request事件,监听客户端的请求
server.on('request', function(req, res) {
        console.log('someone visit our web server')
    })
    //启动服务器
server.listen(8080, function() {
    console.log('server running at http://127.0.0.1:8080')
})

req请求对象

  • 只要服务器接受到了客户端的请求,就会调用通过server.on()为服务器绑定的request事件处理函数,如果想在事件处理函数中,访问与客户端相关的数据或者属性 可以:
  • req.url是客户端请求的url地址
    req.method是客户端的method请求类型
    
  • const http = require('http')
    const server = http.createServer()
    server.on('request', (req) => {
        const url = req.url
        const method = req.method
        const str = `your request url; is${url},and request method is ${method}`
        console.log(str)
    })
    server.listen(8080, () => {
        console.log('server running at http://127.0.0.1:8080')
    })
    

res响应对象

const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
    const url = req.url
    const method = req.method
    const str = `your request url; is${url},and request method is ${method}`
    console.log(str)
        //调用res.end()向客户端响应一些内容
    res.end(str)
})
server.listen(8080, () => {
    console.log('server running at http://127.0.0.1:8080')
})

解决res返回中文时乱码的问题

const http = require('http')
const server = http.createServer()
server.on('request', (req, res) => {
    const url = req.url
    const method = req.method
    const str = `请求的地址是${url},请求的方式是 ${method}`
        //调用res.setHeader()方法,设置Content-type响应头,解决中文乱码问题
    res.setHeader('Content-Type', 'text/html; charset=utf-8')
        //调用res.end()向客户端响应一些内容
    res.end(str)
})
server.listen(8080, () => {
    console.log('server running at http://127.0.0.1:8080')
})

根据不同的url返回不同内容

  1. 获取请求的url地址

  2. 设置默认响应内容

  3. 判断用户请求是什么

  4. 设置content-type响应头

  5. 使用res.end()

    let url=....
    if(url===){
    
    }
    

    ====

模块化

概念

  • 是模块化是指解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程,对于整个系统来说,模块是可组合 分解和更换的单元

好处

  • 提高代码的复用性
  • 提高了代码的可维护性
  • 可以实现按需加载

node.js

  • 内置模块 fs path http
  • 自定义模块 用户创建的js
  • const custom=require(./custom.js)
    
  • 第三方模块 由第三方开发出来的模块 并非官方提供出来的

向外共享模块作用域中的成员

module

  • 在每个.js模块中都有一个module对象,他里面存储了和当前模块有关的信息
  • 在自定义模块中,可以使用module.exports对象,将模块内的成员共享出去,供外界使用
  • module.exports
  • 在自定义模块中
  • module.exports.username='zs'
    module.exports.sayhello=function(){
    console.log('hello')
    }
    
    以上为自定义模块
    一下调用
    
    const m=require('./自定义模块')
    console.log(m)
    
  • exports跟module.exports一样

npm与包

  • 第三方模块又叫包

  • /*
    npm i moment@版本号
    版本号2.23.0
    第一位是大版本
    第二位是功能版本
    第三位是bug修复
    */
    
  • /*
    npm init -y
    在执行命令处的目录中,快速新建package.json文件
    项目文件名必须是英文 不能出现空格 
    
    配置文件中的dependencies节点
    专门记录npm
    */
    

    一次性安装所有的包

    /*npm i
    读取package.json文件 然后安装所有包
    */
    
    /*
    卸载包
    npm uninstall
    */
    

devDependencies节点

  • 如果某些包只在项目开发阶段会用到,在项目上线之后不会用到
  • /*npm i 包名 -D*/
    
  • 如果都需要用到 把这些包记录到dependencies节点中

nrm工具

  • 更加方便的切换下包的镜像源 利用nrm提供的终端命令,可以快速查看和切换下包的镜像源
  • /*
    npm i nrm -g
    nrm ls 查看所有镜像源
    nrm use taobao
    */
    

项目包

  • 那些被安装在项目的node_modules目录中的包 都是项目包
  • 分为两类开发依赖包 记录到Devdependencies中的 只在开发期间会用到
  • 核心依赖包 被记录到dependencies节点中的包 在开发期间和项目上线后都会用到

i5ting_toc

/*i5ting_toc-f 要转换的md文件路径 -O
把md文件转换为html
*/

开发属于自己的包

function dateFormat(dtStr) {
    const dt = new Date(dtStr)
    const y = padzero(dt.getUTCFullYear())
    const m = padzero(dt.getMonth() + 1)
    const n = padzero(dt.getDate())
    const hh = padzero(dt.getHours())
    const mm = padzero(dt.getMinutes())
    const ss = padzero(dt.getSeconds())

    return `${y}-${m}-${n} ${hh}:${mm}:${ss}`
}

function padzero(n) {
    return n > 9 ? n : '0' + n
}
//定义转移html字符的函数
function htmlEscape(htmlstr) {
    return htmlstr.replace(/<|>"|&/g, (match) => {
        switch (match) {
            case '<':
                return '&lt;'

            case '>':
                return '&gt;'
            case '"':
                return '&quot;'
            case '&':
                return '&amp;'
        }
    })
}
//定义还原html字符的函数
function htmlunEscape(htmlstr) {
    return htmlstr.replace(/&lt;&quot;|&amp;/g, (match) => {
        switch (match) {
            case '&lt;':
                return '<'

            case '&gt;;':
                return '>'
            case '&quot;':
                return '&quot;'
            case '&':
                return '&amp;'
        }
    })
}
module.exports = {
    dateFormat,
    htmlEscape,
    htmlunEscape
}

讲不同功能进行模块化拆分

  1. 将格式化时间的功能,拆分
  2. 将处理字符串的功能 拆分
  3. 在index.js中 导入两个模块 得到需要向外共享的方法
  4. 在index.js中 使用module.exports 把对应的方法共享出去
  • 在发布包的时候 要登录到官方地址 不可以镜像
  • /*登录 npm login*/
    
  • /*发布 npm publish*
    删除 npm unpublish 包名 --force 只可以删除72小时之内的
    /
    

模块的加载机制

  • 优先从缓存中加载,模块在第一次加载后会被缓存 这也意味着多次调用require()不会导致模块的代码被执行多次
  • 内置模块优先机制最高

express

认识express

  • web框架
  • 本质是npm的一个第三方包
  • 安装 npm i express
const express = require('express')
const app = express()
app.listen(8080, () => {
    console.log('express server running at http://127.0.0.1:8080')
})

请求get post

app.get('./user', (req, res) => {
    res.send({ name: 'sss', age: 20 })
})
app.post('./user', (req, res) => {
    res.send({ name: 'sss', age: 20 })
})

获取url携带的查询参数

req.query

获取动态参数

req.params

app.get('./user:id', (req, res) => {
    console.log(req.params)
    res.send(req.params)
})

:id是动态参数

托管静态资源

/*express.static()通过他 我们可以非常方便的创建一个静态资源服务器 

app.use(express.static('public')) 
就可以访问public中所有文件了*/
const express = require('express')
const app = express()
app.use(express.static('../前端/node.js—资料/day3/素材/clock'))
app.listen(9090, () => {
        console.log('express server running at http://127.0.0.1:9090')
    })

Express路由

在Express中 路由指客户端的请求与服务器处理函数之间的映射关系 分为三部分 请求的类型 请求的url地址 处理函数

const express = require('express')
const app = express()
app.get('/', (req, res) => {
    res.send('hello word')
})
app.post('/', (req, res) => {
    res.send('post request')
})
app.listen(8080, () => {
    console.log('http://127.0.0.1:8080')
})

模块化管理

  • 为了方便管理 不建议将路由直接挂载到app上,而是推荐将路由由抽离为单独的模块

    1. 创建路由模块对应的.js文件
    2. 调用express.router()函数创建路由对象
    3. 向路由对象上挂载具体的路由
    4. 使用module.exports向外共享路由对象
    5. 使用app.use()函数注册路由模块
    //这是路由模块
    const express = require('express')
        //创建路由对象
    const router = express.Router()
        //挂载具体的路由
    router.get('/user/list', (req, res) => {
        res.send('get your list')
    })
    router.post('/user/list', (req, res) => {
            res.send('add your list')
        })
        //向外导出路由对象
    module.exports = router
    
    ==========================================================================
       //注册路由模块的方法
    ==========================================================================
    const express = require('express')
    const app = express()
        //导入路由
    const router = require('./router')
        //注册路由模块
    app.use(router)
    app.listen(8080, () => {
        console.log('http://127.0.0.1:8080')
    })
    
  • 为路由挂载统一的访问前缀

    app.use('地址' router)
    

Express的中间件

  • 中间环节
  • 当一个请求到达express的服务器之后 可以连续调用多个中间件 从而对这次请求进行预处理
  • 中间件处理函数 function函数中必须有 req res next函数 路由处理函数只有req res
  • next函数的作业 他表示把流转关系转交给下一个中间件或路由
const express = require('express')
const app = express()
    //定义一个最简单的中间件函数
const mw = function(req, res, next) {
    console.log('最简单的中间件')
    next()
}
app.listen(8080, () => {
    console.log('http://127.0.0.1:8080')
})

全局中间件

将中间件注册为全局

app.ues(function(req, res, next) {
    console.log('最简单的中间件')
    next()
})

定义多个全局中间件(按顺序调用)

app.ues(function(req, res, next) {
    console.log('最简单的中间件1')
    next()
})
app.ues(function(req, res, next) {
    console.log('最简单的中间件2')
    next()
})
app.get('./', (req, res) => {
    res.send('home page')
})

局部中间件

const m1=function(req, res, next) {
    console.log('最简单的中间件1')
    next()
}
const m2=function(req, res, next) {
    console.log('最简单的中间件2')
    next()
}
app.get('./',m1,m2, (req, res) => {
    res.send('home page')
})

作用

  • 多个中间件之间 共享同一份req和res 基于这样的特性 我们可以在上游的中间件中 统一为req或者res对象添加自定义的属性或方法 供下游的中间件或者路由使用

    const express = require('express')
    const app = express()
        //定义一个最简单的中间件函数
    const mw = function(req, res, next) {
        const time = Date.now()
            //为req对象挂载自定义属性 从而把时间共享给后面的所有路由
        req.startTime = time
        next()
    }
    app.get('./', (req, res) => {
        res.send('home page' + req.startTime)
    })
    app.listen(8080, () => {
        console.log('http://127.0.0.1:8080')
    })
    

注意

  • 一定要在路由之前注册
  • 客户端发送过来的请求,可以连续调用多个中间件处理
  • 执行完中间件的业务代码之后,不要忘记调用next()函数
  • 为了防止代码逻辑混乱 调用next()函数后不要再写额外的代码
  • 连续调用多个中间件时,多个中间件之间,共享req和res对象

中间件的分类

应用级别
  • 通过app.use() app.get() 绑定到app实例上的中间件 叫做应用级别的中间件
路由级别
  • 绑定到express.router()实例上的中间件 叫做路由级别的中间件
错误级别
  • 专门来捕获整个项目中发生的异常错误,从而防止项目异常崩溃的问题
  • function处理函数中,必须有4个形参 形参顺序从前到后,分别是(err,req,res,next)
  • 所有错误级别的中间件都要保存在路由之后*(与其他级别的中间件不同)*
Express内置
  • express.static 快速托管静态资源
  • express.json 解析json格式的请求体数据
  • const express = require('express')
    const app = express()
    app.use(express.json())
    app.post('./', (req, res) => {
        console.log(req.body)
        res.send('ok')
    })
    
  • express.urlencoded 解析url-ecoded格式的请求体数据
  • app.use(express.urlencoded({ extended: false }))
    app.post('./user', (req, res) => {
        console.log(req.body)
        res.send('ok')
    })
    
第三方
  • body-parser 解析请求体数据
  • const express = require('express')
    const app = express()
        //导入
    const parser = require('body-parser')
        //使用app.use()注册中间件
    app.use(parser.urlencoded({ extended: false }))
    app.post('./', (req, res) => {
        //如果没有配置任何解析表单数据的中间件,则req.body 默认等于undefined
        res.send(req.body)
    })
    app.listen(8080, function {
        console.log('http://127.0.0.1:8080')
    })
    

自定义中间件

  1. 定义中间件

  2. 监听req的data事件

    const express = require('express')
    const app = express()
    app.use((req, res, next) => {
        //定义zhongj件具体的业务逻辑
        //定义一个str字符串 专门用来存储客户端方送过来的请求体数据
        let str = ''
            //监听req的data事件
        req.on('data', (chunk) => {
            str += chunk
        })
    })
    
  3. 监听req的end事件

  4. 使用querysring模块解析请求体数据

    • querystring 专门用来处理查询字符串的 通过这个模块提供的parse()函数 可以轻松把查询字符串解析成对象的格式

      const express = require('express')
      const app = express()
      const qs = require('URLSearchParams')
      app.use((req, res, next) => {
          //定义zhongj件具体的业务逻辑
          //定义一个str字符串 专门用来存储客户端方送过来的请求体数据
          let str = ''
              //监听req的data事件
          req.on('data', (chunk) => {
              str += chunk
          })
          req.on('end', () => {
              //console.log(str)
              //todo把字符串格式的请求体数据 解析成对象格式
              const body = qs.parse(str)
              console.log(body)
          })
      })
      
  5. 将解析出来的数据对象挂载为req.body

    const express = require('express')
    const app = express()
    const qs = require('URLSearchParams')
    app.use((req, res, next) => {
        //定义zhongj件具体的业务逻辑
        //定义一个str字符串 专门用来存储客户端方送过来的请求体数据
        let str = ''
            //监听req的data事件
        req.on('data', (chunk) => {
            str += chunk
        })
        req.on('end', () => {
            //console.log(str)
            //todo把字符串格式的请求体数据 解析成对象格式
            const body = qs.parse(str)
            req.body = body
            next()
        })
    })
    
    app.post('./user', (req, res) => {
        res.send(req.body)
    })
    
  6. 将自定义中间件封装为模块

使用express写接口

get/post

const { urlencoded } = require('body-parser')
const express = require('express')
const app = express()
    //配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))
const router = require('./apiRouter')
    //把路由模块 注册到app上
app.use('/api', router)

nodemon

  • 能够监听项目文件的变动,当代吗被修改后 nodemon会自动帮我们重启项目

cors解决接口跨域问题

  • jsonp也可以解决 但是jsonp无法解决post 只可以get

方法:

  1. 运行npm i cors
  2. const cors=require(‘cors’)
  3. app.use(cors())
<body>
    <button id="btnget">GET</button>
    <button id="btnpost">post</button>
    <script>
        $(function() {
            $('#btnget').on('click', function() {
                $.ajax({
                    type: 'get',
                    url: 'http://127.0.0.1/api/get',
                    data: {
                        name: 'zs',
                        age: 20
                    },
                    seccess: function(res) {
                        console.log(res)
                    },
                })
            })
            $('#btnpost').on('click', function() {
                $.ajax({
                    type: 'post',
                    url: 'http://127.0.0.1/api/post',
                    data: {
                        bookname: 'zs',
                        name: 20
                    },
                    seccess: function(res) {
                        console.log(res)
                    }
                })
            })
        })
    </script>
</body>

const { urlencoded } = require('body-parser')
const express = require('express')
const app = express()
    //配置解析表单数据的中间件
app.use(express.urlencoded({ extended: false }))
const cors = require('cors')
app.use(cors())
const router = require('./apiRouter')
    //把路由模块 注册到app上
app.use('/api', router)

cors响应头

res.setHeader('Access-Control-Allow-Methods','POST,GET,DELETE,HEAD')

cors请求的分类

  • 简单请求

    • 请求方式是get post head
    • 客户端与服务器之间只会发生一次请求
  • 预检请求

    • 请求方式是三个之外的
    • 请求头中包含自定义头部字段
    • 向服务器发送了application/json格式的数据
    • 客户端与服务器之间发生两次请求

jsonp接口

概念

  • 浏览器端通过script标签的src属性 请求服务器上的数据 同时 服务器返回一个函数的调用 这种请求数据的方式叫做jsonp

特点

  • jsonp不属于真正的Ajax请求 因为没有使用xmlhttprequest对象
  • jsonp仅支持get请求 不支持post put delete等请求

步骤

  • 获取客户端发送过来的回调函数的名字
  • 得到要通过jsonp形式发送给客户端的数据
  • 根据前两部得到的数据 拼接出一个函数调用的字符串
  • 坝上一步拼接得到的字符串 响应给客户端的 script标签进行解析执行
app.get('/api/jsonp', (req, res) => {
    //定义JSONP接口具体的实现过程
    const funcName = req.query.callback
    const data = { name: 'zd', age: 22 }
    const scriptstr = `${funcName}(${JSON.stringify(data)})`
    res.send(scriptstr)
})

前后端的身份认证

分类

基于服务器渲染的web开发模式

  • 服务器发送给客户端的html的页面 实在服务器通过字符串的拼接 动态生成的 因此 客户端不需要使用Ajax这样的技术额外请求页面的数据
  • 优点:前端耗时少 有利于SEO
  • 缺点 占用服务器端资源 不利于前后端分离 开发效率低

前后端分离的web开发模式

  • 后端提供接口 前端用Ajax调用接口
  • 优点:开发体验好 用户体验好 减轻了服务器端的渲染压力
  • 缺点:不利用网站的SEO 可以利用Vue提供的ssr技术能够很好的解决SEO问题

如何选择

企业级的网站 主要功能是展示而没有复杂的交互 并且需要良好的SEO 则这时我们就需要使用服务器端的渲染

后台管理项目 交互性比较强 不需要考虑SEO 那么就可以使用前后端分离的开发模式

身份认证

又称身份验证 鉴权 是指通过一定的手段 完成对用户身份的确认

session认证机制

http协议的无状态性 是指客户端的每次http请求都是独立的 连续多个请求之间没有直接的关系 服务器不会主动保留每次http请求的状态

如何突破http的无状态的限制 --------------------------- cookie

cookie

是储存在用户浏览器中的一段不超过4kb的字符串 由一个名称 一个值 和其他几个用于控制cookie有效期 安全性 使用范围的可选属性组成

  • 客户端第一次请求服务器的时候 服务器通过响应头的形式,想客户端发送一个身份认证的cookie 客户端会自动将cookie保存在浏览器中 随后 在客户端浏览器每次请求服务器的时候 浏览器会自动将身份认证相关的cookie 通过请求头的形式 发送给服务器 服务器即可验证客户端的身份

  • cookie不具备安全性 不用拿cookie写隐私信息

  • 提高身份认证的安全性

    session认证机制

在express中使用session认证

// 导入 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')
})

JWT认证机制

session机制要配合cookie才能实现 由于cookie默认不支持跨域访问 所以 当涉及到前端跨域请求后端接口的时候 需要做很多额外的配置 才能实现跨域session认证

  • 当前端请求后端接口不存在跨域问题的时候 推荐使用session 身份
  • JSON Web Token
  • 通常由三部分组成 分别是 header payload(有效荷载) signature(签字)
  • payload是真正的加密信息 header signature是保证安全性的
  • 客户端收到服务器返回的jwt之后 通常会将它储存在localstorage或者sessionstorage中
  • 推荐的做法是把jwt放在http请求头的authorization字段中
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()

// TODO_01:安装并导入 JWT 相关的两个包,分别是 jsonwebtoken 和 express-jwt
const jwt = require('jsonwebtoken')
const expressJWT = require('express-jwt')

// 允许跨域资源共享
const cors = require('cors')
app.use(cors())

// 解析 post 表单数据的中间件
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))

// TODO_02:定义 secret 密钥,建议将密钥命名为 secretKey
const secretKey = 'itheima No1 ^_^'

// TODO_04:注册将 JWT 字符串解析还原成 JSON 对象的中间件
// 注意:只要配置成功了 express-jwt 这个中间件,就可以把解析出来的用户信息,挂载到 req.user 属性上
app.use(expressJWT({ secret: secretKey }).unless({ path: [/^/api//] }))

// 登录接口
app.post('/api/login', function (req, res) {
  // 将 req.body 请求体中的数据,转存为 userinfo 常量
  const userinfo = req.body
  // 登录失败
  if (userinfo.username !== 'admin' || userinfo.password !== '000000') {
    return res.send({
      status: 400,
      message: '登录失败!',
    })
  }
  // 登录成功
  // TODO_03:在登录成功之后,调用 jwt.sign() 方法生成 JWT 字符串。并通过 token 属性发送给客户端
  // 参数1:用户的信息对象
  // 参数2:加密的秘钥
  // 参数3:配置对象,可以配置当前 token 的有效期
  // 记住:千万不要把密码加密到 token 字符中
  const tokenStr = jwt.sign({ username: userinfo.username }, secretKey, { expiresIn: '30s' })
  res.send({
    status: 200,
    message: '登录成功!',
    token: tokenStr, // 要发送给客户端的 token 字符串
  })
})

// 这是一个有权限的 API 接口
app.get('/admin/getinfo', function (req, res) {
  // TODO_05:使用 req.user 获取用户信息,并使用 data 属性将用户信息发送给客户端
  console.log(req.user)
  res.send({
    status: 200,
    message: '获取用户信息成功!',
    data: req.user, // 要发送给客户端的用户信息
  })
})

// TODO_06:使用全局错误处理中间件,捕获解析 JWT 失败后产生的错误
app.use((err, req, res, next) => {
  // 这次错误是由 token 解析失败导致的
  if (err.name === 'UnauthorizedError') {
    return res.send({
      status: 401,
      message: '无效的token',
    })
  }
  res.send({
    status: 500,
    message: '未知的错误',
  })
})

// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(8888, function () {
  console.log('Express server running at http://127.0.0.1:8888')
})
  • 定义secret秘钥 用于加密 解密

在登录成功后生成jwt字符串

  • 调用jsonwebtoken 包提供sign()方法 将用户的信息加密成jwt字符串 响应给客户端
  • jwt.sign()生成jwt字符串 三个参数分别是 用户信息对象 加密秘钥 配置对象
    

注册登录案例