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() // 实例化 koaapp.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.jsconst 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) {// 这里返回的函数,就是上文中的 fnMiddlewarereturn function (context, next) {let index = -1return dispatch(0)function dispatch(i) {if (i <= index) return Promise.reject(new Error('next() called multiple times'))index = i// 取出第 i 个中间件为 fnlet 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 对象传递给中间件函数 fnMiddlewarereturn 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 的 idcosnt { 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 & ageconst { 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 中的 Authorconst { 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.paramsctx.body = {status: 200,message: 'success',data: {id,nickname: 'Simon',age: 18,jobs: '前端攻城狮',skills: '搬砖'}}
})router.post('/user', async ctx => {const { name, age } = ctx.request.bodyctx.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.mzph.cn/bicheng/54341.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机组成原理(笔记3)

IEEE754浮点数标准 这里只讲32位单精度 S——尾数符号&#xff0c;0正1负&#xff1b; M——尾数, 纯小数表示, 小数点放在尾数域的最前面。 一般采用原码或补码表示。 E——阶码&#xff0c;采用“移码”表示; 阶符采用隐含方式&#xff0c;即采用“移码”方法来表示正负指数…

Python 之数据库操作(Python Database Operations)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

基于SSM的在线家用电器销售系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSSMVueMySQL的在线家…

统信服务器操作系统【1050e版】安装手册

统信服务器操作系统1050e版本的安装 文章目录 功能概述一、准备环境二、安装方式介绍安装步骤步骤一:制作启动盘步骤二:系统的安装步骤三:安装引导界面步骤四:图形化界面安装步骤五:选择安装引导程序语言步骤六:进入安装界面步骤七:设置键盘步骤八:设置系统语言步骤九:…

链接升级:Element UI <el-link> 的应用

链接升级&#xff1a;Element UI 的应用 一 . 创建文字链接1.1 注册路由1.2 创建文字链接 二 . 文字链接的属性2.1 文字链接的颜色2.2 是否显示下划线2.3 是否禁用状态2.4 填写跳转地址2.5 加入图标 在本篇文章中&#xff0c;我们将深入探索Element UI中的<el-link>组件—…

Elasticsearch基础(七):Logstash如何开启死信队列

文章目录 Logstash如何开启死信队列 一、确保 Elasticsearch 输出插件启用 DLQ 支持 二、配置 Logstash DLQ 设置 三、查看死信队列 四、排查 CSV 到 Elasticsearch 数据量不一致的问题 Logstash如何开启死信队列 在 Logstash 中&#xff0c;死信队列&#xff08;Dead Le…

C++ nullptr 和NULL的区别

个人主页&#xff1a;Jason_from_China-CSDN博客 所属栏目&#xff1a;C系统性学习_Jason_from_China的博客-CSDN博客 所属栏目&#xff1a;C知识点的补充_Jason_from_China的博客-CSDN博客 概念概述&#xff1a; 在C中&#xff0c;nullptr 和 NULL 都是用来表示空指针&#xf…

微波无源器件 功分器3 一种用于多端口辐射单元的紧凑四路双极化正交模功分器的设计

摘要&#xff1a; 一种有着双极化能力并且能作为一个Fabry-Perot谐振腔天线的馈源包含四个输入端口的新型紧凑功分器的概念和设计被提出了。在四个圆波导中的双同相极化通过使用四个5端口十字转门结合两个8by1&#xff08;八合一&#xff09; 功分网络。功分器末端接了两个端口…

【RabbitMQ】工作模式

工作模式概述 简单模式 简单模式中只存在一个生产者&#xff0c;只存在一个消费者。生产者生产消息&#xff0c;消费者消费消息。消息只能被消费一次&#xff0c;也称为点对点模式。 简单模式适合在消息只能被单个消费者处理的场景下存在。 工作队列模式&#xff08;Work Qu…

项目管理 | 一文读懂什么是敏捷开发管理

在快速变化的商业环境中&#xff0c;项目管理方式也在不断演进&#xff0c;其中敏捷开发管理因其高效、灵活和适应性强的特点&#xff0c;逐渐成为众多企业和团队的首选。本文将详细解析敏捷开发管理的定义、具体内容及其核心角色&#xff0c;帮助读者全面理解这一先进的项目管…

心觉:不能成事的根本原因

很多人一直都很努力&#xff0c;每天都很忙 每天都学习很多东西&#xff0c;学习各种道&#xff0c;各种方法论 但是许多年过去了依然一事无成 自己的目标没有达成&#xff0c;梦想没有实现 为什么呢 关键是没有开悟 那么什么是开悟呢 现在很多人都在讲开悟 貌似开悟很…

阿里云 Quick BI使用介绍

Quick BI使用介绍 文章目录 阿里云 Quick BI使用介绍1. 创建自己的quick bi服务器2. 新建数据源3. 上传文件和 使用4. 开始分析 -选仪表盘5. 提供的图表6. 一个图表的设置使用小结 阿里云 Quick BI使用介绍 Quick BI是一款全场景数据消费式的BI平台&#xff0c;秉承全场景消费…

AI逻辑推理入门

参考数据鲸 (linklearner.com) 1. 跑通baseline 报名 申领大模型API 模型服务灵积-API-KEY管理 (aliyun.com) 跑通代码 在anaconda新建名为“LLM”的环境,并安装好相应包后,在jupyter notebook上运行baseline01.ipynb 2. 赛题解读 一般情况下,拿到一个赛题之后,我们需…

CleanClip vs 传统剪贴板:究竟谁更胜一筹?

在日常工作和生活中,复制粘贴可以说是我们使用最频繁的操作之一。传统的剪贴板功能虽然简单易用,但在功能性和效率上还有很大的提升空间。今天,我们就来比较一下新兴的剪贴板增强工具CleanClip与传统剪贴板,看看到底谁更胜一筹。 1. 剪贴历史管理 传统剪贴板只能存储最后一次…

PaddleNLP本文分类及docker部署流程

本文记录使用PaddleNLP进行文本分类的全流程 参考&#xff1a;https://github.com/PaddlePaddle/PaddleNLP/tree/develop/legacy/applications/text_classification/multi_class 文章目录 1. 数据准备2. 模型训练2.1 准备关键库2.2 模型训练&#xff06;验证2.3 模型测试2.4 结…

分布式中间件-redis相关概念介绍

文章目录 什么是redis?示意图Redis的主要特点Redis的主要用途Redis的工作原理Redis的持久化与备份 redis 6.x新增特性多线程数据加载客户端缓存新的 RESP 3 协议支持ACL&#xff08;Access Control List&#xff09;功能新增数据类型性能改进配置文件的改进其他改进 redis数据…

02 基于STM32的按键控制继电器驱动电机

本专栏所有源资料都免费获取&#xff0c;没有任何隐形消费。 注意事项&#xff1a;STM32仿真会存在各种各样BUG&#xff0c;且尽量按照同样仿真版本使用。本专栏所有的仿真都采用PROTEUS8.15。 本文已经配置好STM32F103C8T6系列&#xff0c;在PROTUES仿真里&#xff0c;32单片…

Doker学习笔记--黑马

介绍&#xff1a;快速构建、运行、管理应用的工具 在不同的服务器上部署多个应用&#xff0c;但是往往不同应用之间会有冲突&#xff0c;因为它们所依赖的环境&#xff0c;函数库&#xff0c;配置都不一样&#xff0c;此时docker在运行时形成了一个隔离环境&#xff08;容器&am…

【C++篇】C++类与对象深度解析(三):类的默认成员函数详解

文章目录 【C篇】C类与对象深度解析&#xff08;三&#xff09;前言4. 运算符重载基本概念4.1 运算符重载的基本概念4.2 重载运算符的规则4.3 成员函数重载运算符4.4 运算符重载的优先级与结合性4.5 运算符重载中的限制与特殊情况4.5.1 不能创建新的操作符4.5.2 无法重载的运算…

QT 带箭头的控件QPolygon

由于对当前项目需要绘制一个箭头控件&#xff0c;所以使用了QPainter和QPolygon来进行绘制&#xff0c;原理就是计算填充&#xff0c;下面贴出代码和效果图 这里简单介绍下QPolygon QPolygon是继承自 QVector<QPoint>那么可以很简单的理解为&#xff0c;他就是一个点的…