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.xdnf.cn/news/143959.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

二叉树的前中后序遍历(迭代法)( 含leetcode上三道【前中后序】遍历题目)

文章目录 前序遍历&#xff08;迭代法&#xff09;中序遍历&#xff08;迭代法&#xff09;后序遍历&#xff08;迭代法&#xff09;总结 为什么可以用迭代法&#xff08;非递归的方式&#xff09;来实现二叉树的前后中序遍历呢&#xff1f; 在队列与栈专题我们就感受到了&…

KeyCode及KeyCode分发机制

文章目录 需求场景纯KeyCode 快捷操作KeyCode 按键响应操作、拦截 一、工作中常用KeyCode二、KeyCode大全三、KeyCode 响应事件事件输入流程事件响应源码分析源码举例说明 需求场景 纯KeyCode 快捷操作 经常在代码中实现返回、Home 、音量加减、截屏 等功能实现&#xff0c;代…

SpringBoot(40) — SpringBoot整合MyBatis-plus

前言 在上节中我们对MyBatis-plus特性有了一个整体的认识&#xff0c;然后也大致讲了些MyBatis与MyBatis-plus的不同之处。大家感兴趣的话&#xff0c;可参考以下文章 SpringBoot(39) — MyBatis-plus简介 这节我们来讲讲SpringBoot项目如何快速接入MyBatis-plus框架。 今天涉及…

开源网安多城联动、多形式开展网安周公益活动,传播网络安全知识

9月9日至15日&#xff0c;以“网络安全为人民&#xff0c;网络安全靠人民”为主题的2024年国家网络安全宣传周将在全国范围内统一开展&#xff0c;通过多样的形式、丰富的内容&#xff0c;助力全社会网络安全意识和防护技能提升。开源网安今年继续为各地企业、群众带来了丰富的…

BOE(京东方)领先科技赋能体育产业全面向新 以击剑、电竞、健身三大应用场景诠释未来健康运动新生活

巴黎全球体育盛会虽已闭幕&#xff0c;但世界范围内的运动热潮并未消退。9月12日&#xff0c;在北京恒通国际商务园&#xff08;UBP&#xff09;的之所ICC&#xff0c;BOE&#xff08;京东方&#xff09;开启了以“屏实力 FUN肆热爱”为主题的“科技赋能体育”互动体验活动。活…

esp32 wifi 联网后,用http 发送hello 用pc 浏览器查看网页

参考chatgpt Esp32可以配置为http服务器&#xff0c;可以socket编程。为了免除编写针对各种操作系统的app。完全可以用浏览器仿问esp32服务器&#xff0c;获取esp32的各种数据&#xff0c;甚至esp的音频&#xff0c;视频。也可以利用浏览器对esp进行各种操作。但esp不能主动仿…

vue3前端开发-小兔鲜超市-本地购物车列表页面的统计计算

vue3前端开发-小兔鲜超市-本地购物车列表页面的统计计算&#xff01;这一次&#xff0c;实现了一些本地购物车列表页面的&#xff0c;简单的计算。 代码如下所示&#xff1a; import { computed, ref } from vue import { defineStore } from pinia export const useCartStor…

web基础—dvwa靶场(八)SQL Injection(Blind)

SQL Injection(Blind)&#xff08;SQL注入之盲注&#xff09; SQL Injection(Blind)&#xff0c;SQL盲注&#xff0c;相比于常规的SQL注入&#xff0c;他不会将返回具体的数据信息或语法信息&#xff0c;只会将服务器包装后的信息返回到页面中。 常规SQL注入与SQL盲注详细对比…

css 样式简单学习(一)

目录 1. css 介绍 1.1 css 样式 1.2 css代码风格 1.2.1 书写格式 1.2.2 样式大小写​编辑 1.2.3 空格规范 2. 基础选择器 2.1 选择器的作用​编辑 2.2 选择器的分类 2.3 基础选择器 2.3.1 标签选择器​编辑 2.3.2 类选择器​编辑 2.3.3 类选择器-多类名​编辑 2.…

Unity 百度AI实现无绿幕拍照抠像功能(详解版)

目录 一、前言 1.抠像效果 2.去哪找百度ai抠图 3.基础流程跳过 二、获取AccessToken 1.什么是Token 2.为什么要获取Token 3.如何获取token 4.解析json 5.完整代码 三、抠像 1.准备地址 2.建立链接&#xff0c;和基本配置 3.图片格式转换 4.开始上传 5.获取回复…

python本地进程通讯----共享内存变量

背景 最近在开发实践中&#xff0c;接触到了需要多进程开发的场景。众所周知&#xff0c;进程和线程最大的区别就在于&#xff1a;进程是资源分配的最小单位&#xff0c;线程是cpu调度的最小单位。对于多进程开发来说&#xff0c;每一个进程都占据一块独立的虚拟内存空间&#…

Mobile net V系列详解 理论+实战(2)

Mobilenet 系列 实践部分一、数据集介绍二、模型整体框架三、模型代码详解四、总结 实践部分 本章针对实践通过使用pytorch一个实例对这部分内容进行吸收分析。本章节采用的源代码在这里感兴趣的读者可以自行下载操作。 一、数据集介绍 可以看到数据集本身被存放在了三个文件…

django学习入门系列之第十点《A 案例: 员工管理系统10》

文章目录 12 管理员操作12.4 密码加密12.5 获取对象&#xff08;防止id错误--编辑界面等&#xff09;12.6 编辑管理员12.7 重置密码 往期回顾 12 管理员操作 12.4 密码加密 密码不应该以明文的方式直接存储到数据库&#xff0c;应该加密才放进去 定义一个md5的方法&#xff…

4 html5 web components原生组件详细教程

web components 前面我们已经介绍过&#xff0c;这一期我们就来讲一讲具体用法和这其中的关键只是点&#xff1a; 1 基本使用 如果我们想实现一个封装的原生组件&#xff0c;那就离不开使用js去封装&#xff0c;这里主要就是基于HTMLElement这个类&#xff0c;去创建创建一个…

OpenCV运动分析和目标跟踪(2)累积操作函数accumulateSquare()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 将源图像的平方加到累积器图像中。 该函数将输入图像 src 或其选定区域提升到2的幂次方&#xff0c;然后加到累积器 dst 中&#xff1a; dst ( …

Android WebView H5 Hybrid 混和开发

对于故乡&#xff0c;我忽然有了新的理解&#xff1a;人的故乡&#xff0c;并不止于一块特定的土地&#xff0c;而是一种辽阔无比的心情&#xff0c;不受空间和时间的限制&#xff1b;这心情一经唤起&#xff0c;就是你已经回到了故乡。——《记忆与印象》 前言 移动互联网发展…

用Python画一个五星红旗

#codingutf-8 import turtle import mathdef draw_polygon(aTurtle, size50, n3): 绘制正多边形args:aTurtle: turtle对象实例size: int类型&#xff0c;正多边形的边长n: int类型&#xff0c;是几边形 for i in range(n):aTurtle.forward(size)aTurtle.left(360.0/n)de…

esp32-C2 对接火山引擎实现语音转文本(二)

目录 一、 语音转文本初始化 二、 WedStream 事件处理函数 一、 语音转文本初始化 Volcengine_vtt_handle_t Volcengine_Vtt_Init(Volcengine_vtt_config_t *config) {// 管道配置audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();Volcengine_vtt_t *vt…

【从计算机的发展角度理解编程语言】C、CPP、Java、Python,是偶然还是应时代的产物?

参考目录 前言什么是"computer"?计算机的大致发展历程计算机系统结构阶段(1946~1981)计算机网络和视窗阶段(1982~2007)复杂信息系统阶段(2008~today)人工智能阶段 越新的语言是越好的吗、越值得学习吗&#xff1f; 前言 最近读了 《Python语言程序设计基础》 这本书…

数据结构与算法学习day21-回溯法

一、组合 1.题目 . - 力扣&#xff08;LeetCode&#xff09; 2思路 把组合问题抽象成树形结构&#xff08;N叉树&#xff09; 每次从集合中选取元素&#xff0c;可选择的范围随着选择的进行而收缩&#xff0c;调整可选择的范围。 图中可以发现n相当于树的宽度&#xff0c…