• Express 与 Koa 学习


    一、Express

    1.1 安装与简单使用

    npm i express

    // send 方法支持返回代码块、数据(自动转为JSON)、文本
    // 注意:send 只能调用一次,后面再调也不会执行
    const espress = require('express')
    
    const app = espress()
    
    app.get('/', (req, res) => {
      res.send('Hello World!')
    })
    
    app.get('/login', (req, res) => {
      res.send({
        code: 0,
        data: 'ok'
      })
    })
    
    app.listen(3000, () => {
      console.log('server start')
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.2 路由

    1.2.1 字符串模式

    // 匹配 acd 和 abcd
    app.get('/ab?cd', (req, res) => {})
    
    // 匹配 abe 和 abcde
    app.get('/ab(cd)?e', (req, res) => {})
    
    // 匹配 ab/***
    app.get('/ab/:id', (req, res) => {})
    
    // 匹配 abcd、abbcd、abbbcd等
    app.get('/ab+cd', (req, res) => {})
    
    // 匹配 abcd、abxcd、abiahiucd等
    app.get('/ab*cd', (req, res) => {})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    1.2.2 正则模式

    // 匹配任何路径中含有 a 的路径
    app.get('/a/', (req, res) => {})
    
    // 匹配以 fly 结尾的路径
    app.get('/.*fly$/', (req, res) => {})
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1.2.3 路由守卫

    第一种写法

    const espress = require('express')
    
    const app = espress()
    
    // 路由守卫
    app.get('/', (req, res, next) => {
      // 登录鉴权,验证 token
      const isLogin = Math.random() > 0.5
      if (isLogin) {
        next()
      } else {
        res.send('401 error')
      }
    }, (req, res) => {
      res.send('欢迎来到 Home 页面')
    })
    
    app.listen(3000, () => {
      console.log('server start')
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    第二种写法

    const espress = require('express')
    
    const app = espress()
    
    function func1(req, res, next) {
      // 登录鉴权,验证 token
      const isLogin = Math.random() > 0.5
      if (isLogin) {
        next()
      } else {
        res.send('401 error')
      }
    }
    
    function func2(req, res) {
      res.send('欢迎来到 Home 页面')
    }
    
    app.get('/', [func1, func2])
    
    app.listen(3000, () => {
      console.log('server start')
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    1.3 中间件

    1.3.1 应用级中间件

    应用级中间件绑定到 app 对象

    const espress = require('express')
    
    const app = espress()
    
    // 访问任何路径都会先走 func1 的逻辑
    app.use(func1)
    
    function func1(req, res, next) {
      // 登录鉴权,验证 token
      const isLogin = Math.random() > 0.5
      if (isLogin) {
        next()
      } else {
        res.send('401 error')
      }
    }
    
    app.get('/home', (req, res) => {})
    app.get('/login', (req, res) => {})
    
    app.listen(3000, () => {})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.3.2 路由级中间件

    跟应用级中间件一样,只是绑定的对象为 express.Router()

    // index.js
    const espress = require('express')
    
    const app = espress()
    const HomeRouter = require('./HomeRouter')
    
    // 以 /home 开头的路径匹配
    app.use('/home', HomeRouter)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    // HomeRouter.js
    const espress = require('express')
    
    const router = espress.Router()
    
    // 访问 /home 走这里逻辑
    router.get('/', (req, res) => {
      console.log('/home')
      res.send('home')
    })
    
    module.exports = router 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    1.3.3 错误中间件

    // 放在路由注册最后,所有路径匹配不到时
    const espress = require('express')
    
    const app = espress()
    
    app.get('/login', (req, res) => {
      res.send({
        code: 0,
        data: 'ok'
      })
    })
    
    // 上面的路径都匹配不到走这里
    app.use((req, res) => {
      // 抛出 404
      res.status(404).send('404 Not Found')
    })
    
    app.listen(3000, () => {
      console.log('server start')
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    1.3.4 内置中间件

    express.static 是 Express 唯一内置的中间件。基于 serve-static,负责在 Express 应用中提托管静态资源,每个应用可由多个静态目录

    app.use(express.static('public'))
    
    • 1

    1.3.5 第三方中间件

    npm i cookie-parser
    
    • 1
    const CookieParser = require('cookie-parser')
    
    app.use(CookieParser)
    
    • 1
    • 2
    • 3

    1.4 获取请求参数

    1.4.1 get 请求

    const express = require('express')
    
    const app = express()
    
    // 获取get请求参数
    app.get('/', (req, res) => {
      // http://localhost:3000/?username=joe
      console.log(req.query) // { username: 'joe' }
      res.send({
        code: 0,
        data: 'get success'
      })
    })
    
    app.listen(3000, () => {
      console.log('server start')
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    1.4.2 post 请求

    const express = require('express')
    
    const app = express()
    
    // 使用内置中间件解析post参数 --- Content-Type: application/x-www-form-urlencoded 格式
    app.use(express.urlencoded({ extended: true }))
    
    // 使用内置中间件解析post参数 --- Content-Type: application/json 格式
    app.use(express.json())
    
    // 获取post请求参数
    app.post('/login', (req, res) => {
      console.log(req.body) // { username: 'joe', password: '123' }
      res.send({
        code: 0,
        data: 'post success'
      })
    })
    
    app.listen(3000, () => {
      console.log('server start')
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    Content-Type: application/x-www-form-urlencoded 格式
    在这里插入图片描述
    Content-Type: application/json 格式
    在这里插入图片描述

    1.5 静态资源

    // 将 public 文件夹当作静态资源目录,访问的时候可以省略 /public 
    app.use(express.static('public'))
    
    • 1
    • 2

    1.6 ejs 模板

    npm i ejs

    • <% %>,流程控制标签(写的是 if else,for)
    • <%= %>,输出标签(原文输出HTML标签)
    • <%- %>,输出标签(HTML标签会被浏览器解析)
    • <%# %>,注释标签
    • <%- include('header', { isShow: false }) %>,注释标签

    需要在应用中进行如下设置才能让 Express 渲染模板文件:

    1. views,放模板文件的目录,比如: app.set('views', './views')
    2. view engine,模板引擎,比如:app.set('view engine', 'ejs')

    1.6.1 简单使用

    // index.js
    const express = require('express')
    
    const app = express()
    app.set('views', './views')
    app.set('view engine', 'ejs')
    
    
    app.get('/', (req, res) => {
      // 这里会找 ./views 下的 login.ejs 渲染
      res.render('login', { error: '' })
    })
    
    app.listen(3000, () => {
      console.log('server start')
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    // views/login.ejs
    <div>login 页面, <%= error %></div>
    
    • 1
    • 2

    1.6.2 for 循环遍历数据

    // index.js
    app.get('/home', (req, res) => {
      res.render('home', { list: [1, 2, 3] })
    })
    
    • 1
    • 2
    • 3
    • 4
    // views/home.ejs
    <ul>
      <% for(let i = 0; i < list.length; i++) { %>
        <li><%= list[i] %> </li>
      <% } %> 
    </ul>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.7 文件上传

    说明文档
    Multer 是一个 node.js 中间件,用于处理 multipart/form-data 类型的表单数据,它主要用于上传文件

    npm i multer

    const express = require('express')
    const multer  = require('multer')
    // 文件存放在 uploads 文件夹下
    const upload = multer({ dest: 'uploads/' })
    
    const app = express()
    
    app.post('/profile', upload.single('avatar'), function (req, res, next) {
      // req.file 是 `avatar` 文件的信息(前端传文件的字段)
      const filename = `/uploads/${req.file.filename}` // 获取文件之后存数据库
      // req.body 将具有文本域数据,如果存在的话
    })
    
    app.post('/photos/upload', upload.array('photos', 12), function (req, res, next) {
      // req.files 是 `photos` 文件数组的信息
      // req.body 将具有文本域数据,如果存在的话
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    1.8 应用程序生成器

    通过应用生成器工具 express-generator 可以快速创建一个应用的骨架。

    npx express-generator
    
    • 1
    npm install -g express-generator
    express
    
    • 1
    • 2

    创建 ejs 模板项目

    express --view=ejs myapp
    cd myapp
    npm install
    npm start
    
    • 1
    • 2
    • 3
    • 4

    二、Koa

    2.1 简单使用

    npm i koa

    const Koa = require('koa')
    
    const app = new Koa()
    
    // ctx 上下文
    /*
      ctx.req - Node 的 request 对象
      ctx.res - Node 的 response 对象
      ctx.request - koa 的 request 对象
      ctx.response - koa 的 response 对象
    */
    app.use((ctx, next) => {
      // response.body 是往外输出的
      // ctx.body - ctx.response.body 简写方式
      ctx.response.body = 'hello world'
    })
    
    app.listen(3000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    2.2 Koa 对比 Express

    1)更轻量

    • koa 不提供内置的中间件
    • koa 不提供路由,而是把路由这个库分离出来了(koa/router)

    2)Context 对象
    koa 增加了一个 context 对象作为这次请求的上下文对象(在 koa2 中作为中间件的第一个参数传入)。同时 Context 上也挂载了 request 和 response 两个对象

    3)异步流程控制
    express 使用 callback 来处理异步,koa v1 使用 generator,koa v2 使用 async/await

    2.3 Koa 路由

    npm i koa-router

    2.3.1 注册路由

    const Koa = require('koa')
    const KoaRouter = require('koa-router')
    
    const app = new Koa()
    const router = new KoaRouter()
    
    router.get('/', (ctx, next) => {
      ctx.body = 'hello world!'
    })
    
    // allowedMethods 的作用是当请求方式出错时,响应头 Allow 会返回正确的请求方式
    app.use(router.routes()).use(router.allowedMethods())
    
    app.listen(3000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.3.2 分模块注册路由

    1)入口文件

    // 入口文件 app.js
    const Koa = require('koa')
    
    const router = require('./router/index')
    
    const app = new Koa()
    
    app.use(router.routes()).use(router.allowedMethods())
    
    app.listen(3000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2)路由入口文件

    // router/index.js
    const KoaRouter = require('koa-router')
    
    const userRouter = require('./user')
    const listRouter = require('./list')
    
    const router = new KoaRouter()
    
    // 统一加前缀
    // router.prefix('/api')
    
    // 注册路由级组件
    router.use('/user', userRouter.routes(), userRouter.allowedMethods())
    router.use('/list', listRouter.routes(), listRouter.allowedMethods())
    
    // 重定向
    router.redirect('/', '/user')
    
    module.exports = router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3)路由模块文件

    // router/user.js
    const Koa = require('koa')
    const KoaRouter = require('koa-router')
    
    const app = new Koa()
    const router = new KoaRouter()
    
    router.get('/', (ctx, next) => {
      ctx.body = 'user page'
    })
    
    app.use(router.routes()).use(router.allowedMethods())
    
    module.exports = router
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.4 静态资源

    npm i koa-static

    const static = require('koa-static')
    
    // 将 public 文件夹设为静态资源目录
    app.use(static(
      path.join(__dirname, 'public')
    ))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.5 获取请求参数

    2.5.1 get 请求

    // http://localhost:3000/list?page=0&size=10
    router.get('/', (ctx, next) => {
      console.log(ctx.query) // { page: '0', size: '10' }
      ctx.body = 'list page'
    })
    
    // http://localhost:3000/list/123
    router.get('/:id', (ctx, next) => {
      console.log(ctx.params) // { id: '123' }
      ctx.body = 'list page'
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2.5.2 post 请求

    const Koa = require('koa')
    const KoaRouter = require('koa-router')
    const KoaBodyparser = require('koa-bodyparser')
    
    const app = new Koa()
    const router = new KoaRouter()
    
    app.use(KoaBodyparser())
    
    router.post('/list', (ctx, next) => {
      console.log(ctx.request.body)
      ctx.body = 'list'
    })
    app.use(router.routes()).use(router.allowedMethods())
    
    app.listen(3000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.6 ejs 模板

    npm i ejs koa-views

    // 入口文件
    const Koa = require('koa')
    const path = require('path')
    const KoaRouter = require('koa-router')
    const KoaViews = require('koa-views')
    
    const app = new Koa()
    const router = new KoaRouter()
    
    app.use(KoaBodyparser())
    // 配置模板引擎
    app.use(KoaViews(path.join(__dirname, 'views'), { extension: 'ejs' }))
    
    // 异步读取文件,必须使用 async await
    router.get('/', async (ctx, next) => {
      // 渲染 views 文件夹下的 index.ejs
      await ctx.render('index')
    })
    
    app.use(router.routes()).use(router.allowedMethods())
    
    app.listen(3000)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    2.7 cookie 与 session

    2.7.1 cookie

    koa 提供了从上下文直接读取、写入 cookie 的方法

    • ctx.cookies.get(name, [options]) 读取上下文请求中的 cookie
    • ctx.cookies.set(name, value, [options]) 在上下文中写入 cookie

    2.7.2 session

    1)下载 koa-session-minimal
    npm i koa-session-minimal

    2)引入 koa-session-minimal

    const session = require('koa-session-minimal')
    
    app.use(session({
      key: 'sessionId',
      // cookie 属性配置
      cookie: {
        maxAge: 1000 * 60
      }
    }))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3)登录成功设置 session

    // 登录接口,ctx.session 是自动匹配 cookie 生成的对象,不同浏览器生成 session 的对象不同
    router.post('/login', function(ctx, next) {
      const { name, password } = ctx.request.body
      // 成功,ctx.session 添加一个字段记录已登录 例如添加 user 字段
      ctx.session.user = true
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4)设置路由中间件,session 过期校验

    app.use(async (ctx, next) => {
      // login 相关路由
      if (ctx.url.includes('/login')) {
        await next()
        return
      }
    
      // 校验是否有 cookie
      if (ctx.session.user) {
        // 重新设置 session 对象,刷新过期时间
        ctx.session.user = Date.now()
        await next()
      } else {
        // 重定向 login 页面
        ctx.redirect('/login')
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    2.8 文件上传

    说明文档
    npm i @koa/multer multer

    const Koa = require('koa')
    const KoaRouter = require('koa-router')
    const multer  = require('@koa/multer')
    // 文件存放在 uploads 文件夹下
    const upload = multer({ dest: 'uploads/' })
    
    const app = new Koa()
    const router = new KoaRouter()
    
    router.post('/profile', upload.single('avatar'), function (ctx, next) {
      // ctx.file 是 `avatar` 文件的信息(前端传文件的字段)
      const filename = `/uploads/${ctx.file.filename}` // 获取文件之后存数据库
    })
    
    router.post('/photos/upload', upload.array('photos', 12), function (ctx, next) {
      // ctx.files 是 `photos` 文件数组的信息
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
  • 相关阅读:
    【JS重点16】对象原型
    通讯网关软件003——利用CommGate X2Mbt实现Modbus TCP访问OPC Server
    Python实现 关联规则的Apriori 算法-亚马逊购物零售数据挖掘
    形式主语 it 练习题
    grid的使用
    内网离线安装 Rancher 过程记录
    C语言学习系列->联合体and枚举
    两天学会微服务网关Gateway-Gateway路由规则
    SRM系统可以为企业带来什么价值?
    【Python】给定一个长度为n的数列,将这个数列按从小到大的顺序排列。1<=n<=200
  • 原文地址:https://blog.csdn.net/weixin_44257930/article/details/125330030