Koa (下一代web框架) 【Node.js进阶】

koa (中文网) 是基于 Node.js 平台的下一代 web 开发框架,致力于成为应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石;

利用 async 函数 丢弃回调函数,并增强错误处理,koa 没有任何预置的中间件,可快速的编写服务端应用程序。

切入点

    • 一、核心概念
    • 二、初识 koa
    • 三、路由
      • 1、安装依赖:@koa/router
      • 2、定义路由
    • 四、中间件
      • 中间件是如何执行的?
    • 五、常见获取值的几种方式
      • 1、在 params 中取值
      • 2、在 query 中取值
      • 3、获取 header 中的参数
      • 4、 获取 body 中的数据
    • 六、创建 RESTful 接口


一、核心概念

  • Koa Application(应用程序)
  • Context(上下文,可简化为 ctx)
  • Request(请求)、Response(响应)

二、初识 koa

# 创建一个 app-koa 的文件夹,作为 koa 工程根目录
mkdir app-koa

# 进入创建的文件夹
cd app-koa

# 初始化 package.json
npm init -y

# 安装 koa
npm i koa

Tips: 可以在 package.json 中查看安装的所有依赖

在工程目录里创建一个 app.js

const Koa = require('koa') // 引入 koa
const app = new Koa() // 实例化 koa

app.use(ctx => {
  ctx.body = 'hello koa'
})

// 启动应用程序  参数:端口号
app.listen(3000)

在终端中使用 node app.js 命令【注意:执行命令时,终端的路径必须指向当前程序目录】

打开浏览器访问:http://localhost:3000 此时浏览器中就会输出 hello koa

在这里插入图片描述

上面代码虽然轻松实现了一个 web 服务器,但是返回的数据和所请求都是固定的;
并不适应真实的业务场景,比如:获取请求接口时的参数、方法、修改一次代码就要在终端中重新运行启动命令等;

由于使用的 node app.js 启动,所以每次更改都要重新启动,这样给我们开发带来了极大的不便利,所以我们可以使用一些第三方依赖来自动监听文件的变化并重新启动,开发环境可以使用 nodemon ,首先安装 npm i nodemon -D,也可以全局安装此依赖。生产环境的话可以使用 pm2 安装之后在package.jsonscripts 中添加启动方式。如下:

{
  "name": "app-koa",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "koa": "^2.15.3"
  },
  "devDependencies": {
    "nodemon": "^2.0.7"
  }
}

在终端中执行命令: npm run dev 这样就不用我们每次修改都重新启动,执行之后就会在终端中提示,如下:

在这里插入图片描述

三、路由

1、安装依赖:@koa/router

@koa/router

npm i @koa/router

2、定义路由

  1. app.js 中引入 @koa/router ,然后再实例化

    const Router = require('@koa/router')
    const router = new Router({ prefix: '/api/v1' }) // 实例化的时候可以自定义一个接口前缀
    
  2. 注册 router

    app.use(router.routes()).use(router.allowedMethods())
    
  3. 定义接口

    router.get('/', async ctx => {
      ctx.body = {
        status: 200,
        message: 'hello @koa/router'
      }
    })
    
    router.get('/user', async ctx => {
      ctx.body = {
        status: 200,
        message: 'success',
        data: {
          nickname: 'Simon',
          age: 18,
          jobs: '前端攻城狮',
          skills: '搬砖'
        }
      }
    })
    

app.js 完整代码如下:

const Koa = require('koa')
const Router = require('@koa/router')
const app = new Koa()
const router = new Router({ prefix: '/api/v1' }) // 添加接口前缀

router.get('/', async ctx => {
  ctx.body = {
    status: 200,
    message: 'hello @koa/router'
  }
})

router.get('/user', async ctx => {
  ctx.body = {
    status: 200,
    message: 'success',
    data: {
      nickname: 'Simon',
      age: 18,
      jobs: '前端攻城狮',
      skills: '搬砖'
    }
  }
})

app.use(router.routes()).use(router.allowedMethods())
// 启动应用程序  参数:端口号
app.listen(3000)

在浏览器中请求: http://localhost:3000/api/v1http://localhost:3000/api/v1/user,结果如下图:

在这里插入图片描述

在这里插入图片描述

四、中间件

中间件其实就是一个个函数,通过 app.use() 注册;

在 koa 中只会自动执行第一个中间件,后面的都需要我们自己调用,koa 在执行中间件的时候都会携带两个参数 context (可简化为ctx)和 nextcontext 是 koa 的上下文对象,next 就是下一个中间件函数;

这也就是洋葱模型,所谓洋葱模型,就是指每一个 Koa 中间件都是一层洋葱圈,它即可以掌管请求进入,也可以掌管响应返回。

换句话说:外层的中间件可以影响内层的请求和响应阶段,内层的中间件只能影响外层的响应阶段。

koa洋葱模型

执行顺序按照 app.use() 的顺序执行,中间件可以通过 await next() 来执行下一个中间件,同时在最后一个中间件执行完成后,依然有恢复执行的能力。

即,通过洋葱模型,await next() 控制调用 “下游”中间件,直到 “下游”没有中间件且堆栈执行完毕,最终流回“上游”中间件。

下面这段代码的结果就能很好的诠释,示例:

// app.js

const Koa = require('koa')
const app = new Koa()

app.use(async (ctx, next) => {
  console.log(`this is a middleware 1`)
  await next()
  console.log(`this is a middleware 1 end `)
})

app.use(async (ctx, next) => {
  console.log(`this is a middleware 2`)
  await next()
  console.log(`this is a middleware 2 end `)
})

app.use(async (ctx, next) => {
  console.log(`this is a middleware 3`)
  await next()
  console.log(`this is a middleware 3 end `)
})

app.listen(3000)

运行结果如下:

this is a middleware 1
this is a middleware 2
this is a middleware 3
this is a middleware 3 end
this is a middleware 2 end
this is a middleware 1 end

中间件是如何执行的?

// 通过 createServer 方法启动一个 Node.js 服务
listen(...args) {
  const server = http.createServer(this.callback());
  return server.listen(...args);
}

Koa 框架通过 http 模块的 createServer 方法创建一个 Node.js 服务,并传入 this.callback() 方法。

this.callback() 方法源码精简实现如下:

function compose(middleware) {
  // 这里返回的函数,就是上文中的 fnMiddleware
  return function (context, next) {
    let index = -1
    return dispatch(0)

    function dispatch(i) {
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      // 取出第 i 个中间件为 fn
      let fn = middleware[i]

      if (i === middleware.length) fn = next

      // 已经取到了最后一个中间件,直接返回一个 Promise 实例,进行串联
      // 这一步的意义是保证最后一个中间件调用 next 方法时,也不会报错
      if (!fn) return Promise.resolve()

      try {
          // 把 ctx 和 next 方法传入到中间件 fn 中,并将执行结果使用 Promise.resolve 包装
          // 这里可以发现,我们在一个中间件中调用的 next 方法,其实就是dispatch.bind(null, i + 1),即调用下一个中间件
          return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
          return Promise.reject(err)
      }
    }
  }
}

callback() {
  // 从 this.middleware 数组中,组合中间件
  const fn = compose(this.middleware);

  // handleRequest 方法作为 `http` 模块的 `createServer` 方法参数,
  // 该方法通过 `createContext` 封装了 `http.createServer` 中的 `request` 和 `response`对象,并将这两个对象放到 ctx 中
  const handleRequest = (req, res) => {
    const ctx = this.createContext(req, res);
    // 将 ctx 和组合后的中间件函数 fn 传递给 this.handleRequest 方法
    return this.handleRequest(ctx, fn);
  };

  return handleRequest;
}

handleRequest(ctx, fnMiddleware) {
  const res = ctx.res;
  res.statusCode = 404;
  const onerror = err => ctx.onerror(err);
  const handleResponse = () => respond(ctx);
  // on-finished npm 包提供的方法,该方法在一个 HTTP 请求 closes,finishes 或者 errors 时执行
  onFinished(res, onerror);
  // 将 ctx 对象传递给中间件函数 fnMiddleware
  return fnMiddleware(ctx).then(handleResponse).catch(onerror);
}

将 Koa 一个中间件组合和执行流程梳理为以下步骤:

  • 通过 compose 方法组合各种中间件,返回一个中间件组合函数 fnMiddleware
  • 请求过来时,会先调用 handleRequest 方法,该方法完成;
  • 调用 createContext 方法,对该次请求封装出一个 ctx 对象;
  • 接着调用 this.handleRequest(ctx, fnMiddleware) 处理该次请求。
  • 通过 fnMiddleware(ctx).then(handleResponse).catch(onerror) 执行中间件。

五、常见获取值的几种方式

1、在 params 中取值

// 前端请求
await axios.post('http://localhost:3000/api/v1/user/1')

// 服务端接收
router.post('/user/:id',async ctx => {
  // 获取 url 的 id
  cosnt { id } = ctx.params;  // { id: 1 }
})

2、在 query 中取值

也就是获取问号后面的参数

// 前端请求
await axios.post('http://localhost:3000/api/v1/user?name=Simon&age=18')

// 服务端接收
router.post('/user', async ctx => {
  // 获取 url 的 name & age
  const { name, age } = ctx.request.query; // { name: Simon, age: 18 }
})

3、获取 header 中的参数

// 前端 请求接口时设置请求头
axios
  .post(
    'http://localhost:3000/api/v1/user?name=Simon&age=18',
    {
      headers: {
        Author: 'token'
      }
    }
    //......
  )
  .then(res => console.log('res:', res))

// 在服务端 获取
router.post('/user', async ctx => {
  // 获取 header 中的 Author
  const { Author } = ctx.request.header // { Author: 'token' }
})

4、 获取 body 中的数据

在服务端获取 body 中的一些数据只能用一些外部的插件;如:koa-bodykoa-bodyparser 等等。

就以 koa-body 为例,首先安装 npm i koa-body,再引入:

// 前端发起请求有两种传参方式,一种是 json,另一种是 fromData;
// 以 json 为例
axios.post('http://localhost:3000/api/v1/user', {name: 'Simon', age: 18}).then(res => {
    console.log('res:', res)
});

// 服务端
// 引入 koa-body 插件
const body = require('koa-body);

// 然后在注册中间件:
app.use(body());

// 在服务端获取:
router.post('/user', async ctx => {
    const res = ctx.request.body; // { name: 'Simon', age: 18 }
});

六、创建 RESTful 接口

const Koa = require('koa')
const Router = require('@koa/router')
const koaBody = require('koa-body')
const app = new Koa()
const router = new Router({ prefix: '/api/v1' })

router.get('/', async ctx => {
  ctx.body = {
    status: 200,
    message: 'hello @koa/router'
  }
})

router.get('/user', async ctx => {
  ctx.body = {
    status: 200,
    message: 'success',
    data: {
      query: ctx.query,
      nickname: 'Simon',
      age: 18,
      jobs: '前端攻城狮',
      skills: '搬砖'
    }
  }
})

router.get('/user/:id', async ctx => {
  const { id } = ctx.params
  ctx.body = {
    status: 200,
    message: 'success',
    data: {
      id,
      nickname: 'Simon',
      age: 18,
      jobs: '前端攻城狮',
      skills: '搬砖'
    }
  }
})

router.post('/user', async ctx => {
  const { name, age } = ctx.request.body
  ctx.body = {
    status: 200,
    data: {
      name,
      age
    }
  }
})

// koa-body 中间件的引入顺序必须在 @koa/router 之前,否则获取不了 post 请求携带的数据
app.use(koaBody()).use(router.routes()).use(router.allowedMethods())

app.listen(3000)

希望上面的内容对你的工作学习有所帮助!欢迎各位一键三连哦~

各位 加油!

原创不易,还希望各位大佬支持一下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下

👍 点赞,你的认可是我创作的动力! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!

⭐️ 收藏,你的青睐是我努力的方向! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!

✏️ 评论,你的意见是我进步的财富! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/883159.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql安装教程(新手版)

本教程不需要手动设置配置文件&#xff0c;比较简单&#xff0c;适合新手&#xff0c;过程需联网。 1.找到mysql官网 mysql官网 一.mysql的安装 1.界面如下图&#xff0c;点击箭头所指。 2.选择mysql版本&#xff0c;系统&#xff0c;安装。 3.下载完成后双击打开&#xff0…

golang操作mysql利器-gorm

1、傻瓜示例 GORM通过将数据库表中的数据映射到面向对象的模型中&#xff0c;简化了数据库操作&#xff0c;使得开发者可以很方便的使用代码来操作数据库&#xff0c;而无需编写SQL语句。 目前有个mysql表&#xff1a;miniprogram_orders&#xff0c;其存储了所有用户对应的订…

Android SystemUI组件(07)锁屏KeyguardViewMediator分析

该系列文章总纲链接&#xff1a;专题分纲目录 Android SystemUI组件 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节持续迭代之前章节的思维导图&#xff0c;主要关注左侧上方锁屏分析部分即可。 为了更好理解本文的内容&#xff0c;优先说明下SystemUI中与Ke…

CoreDNS实现跨集群service解析实践

CoreDNS实现跨集群service解析实践 背景介绍使用条件实现方案 CoreDNS是一款使用Go语言实现的专为云原生应用而生的DNS服务器。本文介绍CoreDNS在特定实际场景下的一种进阶使用实践&#xff0c;也许能为其他也在使用CoreDNS做服务发现的同学提供一些启发和思考。 背景介绍 在…

luceda ipkiss教程 76:设计光栅耦合器

案例分享&#xff1a;设计光栅耦合器 全部代码如下&#xff1a; from si_fab import all as pdk from ipkiss3 import all as i3 import numpy as npclass grating_coupler(i3.PCell):"""SOI grating coupler."""_name_prefix "grating_c…

mysql 05 InnoDB数据页结构

01.数据页结构的快速浏览 02.记录在页中的存储 在页的7个组成部分中&#xff0c;我们自己存储的记录会按照我们指定的 行格式 存储到 User Records 部分。但是在一开始生成页的时候&#xff0c;其实并没有 User Records 这个部分&#xff0c;每当我们插入一条记录&#xff0c…

单词记忆的化境:用思想的流水去淹没坚硬的石块

其实&#xff0c;鹅卵石通常都是很硬的。但是河底的石子&#xff0c;几乎大多都成了鹅卵石&#xff0c;它们被流水淹没&#xff0c;日复一日、夜以继日的冲刷着&#xff0c;没有了棱角。 在单词的记忆过程中&#xff0c;我们有太多的人&#xff0c;都有着不堪回首的往事&#x…

面试系列-携程暑期实习一面

Java 基础 1、Java 中有哪些常见的数据结构&#xff1f; 图片来源于&#xff1a;JavaGuide Java集合框架图 Java 中常见的数据结构包含了 List、Set、Map、Queue&#xff0c;在回答的时候&#xff0c;只要把经常使用的数据结构给说出来即可&#xff0c;不需要全部记住 如下&…

爬虫逆向学习(九):记录一个集cookie、请求参数、请求体、响应文本加密的站点反爬

此分享只用于学习用途&#xff0c;不作商业用途&#xff0c;若有冒犯&#xff0c;请联系处理 反爬前置信息 站点&#xff1a;aHR0cHM6Ly96d2Z3LmNxLmdvdi5jbi9pY2l0eS9pY2l0eS9lbmdpbmVlcmluZy9uYXZpZ2F0aW9u 接口&#xff1a;/icity/api-v2/cq.app.icity.engineering.Engine…

江科大51单片机

文章目录 led灯led点亮led闪烁流水灯 独立按键按键点灯按键消抖按键实现二进制流水灯按键实现流水灯 数码管静态数码管显示动态数码管显示 矩阵键盘定时器/中断串口通信led点阵屏DS1302实时时钟蜂鸣器AT24C02DS18B20LCD1602直流电机驱动AD/DA红外遥控 led灯 创建项目&#xff…

打印沙漏(最蠢的办法)

直接给代码&#xff0c;很好理解的 #include<bits/stdc.h> using namespace std; int s(int b){if(b<1)return 0;if(b2)return 1;for(int i3;i<sqrt(b);i){if(b%i0)return 0;}return 1; } int main(){int n;cin>>n;char c;cin>>c;vector<int>s;…

网络原理之IP协议(网络层)

目录 前言 什么是IP协议&#xff1f; IP协议的协议头格式 16位总长度&#xff08;字节数&#xff09; 16位标识、3位标志位和13位片偏移 8位生存时间 IP地址管理 1.动态分配IP 2.NAT机制&#xff08;网络地址转换&#xff09; NAT机制是如何工作的 NAT机制的优缺点…

解决启动docker desktop报The network name cannot be found的问题

现象 deploying WSL2 distributions ensuring main distro is deployed: checking if main distro is up to date: checking main distro bootstrap version: getting main distro bootstrap version: open \wsl$\docker-desktop\etc\wsl_bootstrap_version: The network name…

Windows驱动调试方法

单步调试驱动 驱动的调试不能直接在本机上进行&#xff0c;而是要放在虚拟机&#xff08;或其它设备&#xff09;中。这是因为在内核模式下&#xff0c;一个断点的触发将会停下整个系统而不只是单个进程。 在前面的文章里&#xff0c;使用了DbgPrint函数来进行日志的输出&…

基于python+django+vue的旅游景点数据分析系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏&#xff1a;Java精选实战项目…

Elasticsearch 分片迁移与移除集群节点操作

Elasticsearch 分片迁移与移除集群节点操作 问题背景 在单台服务器上部署了 7 个 Elasticsearch 节点&#xff0c;分别为 es-node1 到 es-node7&#xff0c;端口从 9201 到 9207。每个节点都承载大量数据&#xff0c;但没有设置副本分片。由于多个节点共享同一台服务器的硬件…

论文阅读 - SWATTING Spambots: Real-time Detection of Malicious Bots on X

https://web.archive.org/web/20240523035749id_/https://dl.acm.org/doi/pdf/10.1145/3589335.3651564 目录 ABSTRACT INTRODUCTION METHODOLOGY 3 RESULTS ABSTRACT 在 X&#xff08;前身为 Twitter&#xff09;等社交网络平台上&#xff0c;垃圾邮件机器人的活动日益…

【SpringBoot整合Redis测试Redis集群案例】

1、第一步&#xff0c;创建springboot项目&#xff0c;并导入依赖 如图&#xff0c;创建项目遇到的第一个问题就是&#xff0c;当type选择maven&#xff0c;jdk选择1.8时&#xff0c;java部分没办法选择1.8的版本&#xff0c;这怎么办呢&#xff1f; 原因&#xff1a;搜了一下…

【windows 11 安装maven】

从下载网址下载maven 解压&#xff08;路径无中文&#xff09; 配置本地仓库&#xff0c;用来存储jar包 配置仓库路径 配置文件路径&#xff1a;./conf/settings.xml(conf文件夹中的settings.xml文件中&#xff09; 定位到53行修改 <!-- localRepository| The path to the l…

第 1 章:Vue 核心

1. Vue 简介 1.1. 官网 英文官网: https://vuejs.org/中文官网: https://cn.vuejs.org/&#xff1a;中文官网里面【教程】和【API】是比较重要的。用到api就去查询&#xff0c;实践当中记忆更牢靠。 风格指南&#xff1a;官方推荐写的一个代码风格cookbook&#xff1a;编写v…