Koa初体验

news2024/11/20 18:47:43

前面我们已经学习了express,另外一个非常流行的Node Web服务器框架就是Koa。

Koa官方的介绍:

  • koa:next generation web framework for node.js;

  • koa:node.js的下一代web框架;

事实上,koa是express同一个团队开发的一个新的Web框架:

  • 目前团队的核心开发者TJ的主要精力也在维护Koa,express已经交给团队维护了;

  • Koa旨在为Web应用程序和API提供更小、更丰富和更强大的能力;

  • 相对于express具有更强的异步处理能力(后续我们再对比);

  • Koa的核心代码只有1600+行,是一个更加轻量级的框架,我们可以根据需要安装和使用中间件;

1.2. koa初体验

因为学习过了express,它们的基本开发模式是比较相似的。

我们来体验一下koa的Web服务器:

const Koa = require('koa');

const app = new Koa();

app.use((ctx, next) => {
  console.log("middleware 01");
  next();
})

app.use((ctx, next) => {
  console.log("middleware 02");
  ctx.response.body = "Hello World";
})


app.listen(8000, () => {
  console.log("服务器启动成功~");
});

koa注册的中间件提供了两个参数:

  • ctx:上下文(Context)对象;

    • koa并没有像express一样,将req和res分开,而是将它们作为ctx的属性;

    • ctx代表依次请求的上下文对象;

    • ctx.request:获取请求对象;

    • ctx.response:获取响应对象;

  • next:本质上是一个dispatch,类似于之前的next;

    • 后续我们学习Koa的源码,来看一下它是一个怎么样的函数;

koa通过创建的app对象,注册中间件只能通过use方法:

  • Koa并没有提供methods的方式来注册中间件;

  • 也没有提供path中间件来匹配路径;

但是真实开发中我们如何将路径和method分离呢?

  • 方式一:根据request自己来判断;

  • 方式二:使用第三方路由中间件;

方式一:根据request自己判断

app.use((ctx, next) => {
  if (ctx.request.path === '/users') {
    if (ctx.request.method === 'POST') {
      ctx.response.body = "Create User Success~";
    } else {
      ctx.response.body = "Users List~";
    }
  } else {
    ctx.response.body = "Other Request Response";
  }
})

1.3. 路由的使用

koa官方并没有给我们提供路由的库,我们可以选择第三方库:koa-router

1.3.1. 安装koa-router

因为是第三方的库,所以我们需要单独下项目中安装:

npm install koa-router

1.3.2. koa-router基本使用

我们可以先封装一个 user.router.js 的文件:

const Router = require('koa-router');

const userRouter = new Router();

userRouter.get('/users', (ctx, next) => {
  ctx.response.body = "user list~";
});

userRouter.post('/users', (ctx, next) => {
  ctx.response.body = "create user info~";
});

module.exports = userRouter;

 在app中将router.routes()注册为中间件:

app.use(userRouter.routes());
app.use(userRouter.allowedMethods());

注意:allowedMethods用于判断某一个method是否支持:

  • 如果我们请求 get,那么是正常的请求,因为我们有实现get;

  • 如果我们请求 put、delete、patch,那么就自动报错:Method Not Allowed,状态码:405;

  • 如果我们请求 link、copy、lock,那么就自动报错:Not Implemented,状态码:501;

 通常一个路由对象是对一组相似路径的封装,那么路径的前缀都是一直的,所以我们可以直接在创建Router时,添加前缀:

const userRouter = new Router({prefix: '/users'});

userRouter.get('/', (ctx, next) => {
  ctx.response.body = "user list~";
});

userRouter.post('/', (ctx, next) => {
  ctx.response.body = "create user info~";
});

module.exports = userRouter;

1.4. 请求解析

客户端传递到服务器参数的方法常见的是5种:

  • 方式一:通过get请求中的URL的params;

  • 方式二:通过get请求中的URL的query;

  • 方式三:通过post请求中的body的json格式;

  • 方式四:通过post请求中的body的x-www-form-urlencoded格式;

  • 方式五:通过post请求中的form-data格式;

1.4.1. 方式一:params

请求地址:http://localhost:8000/users/123

获取params:

const userRouter = new Router({prefix: "/users"})

userRouter.get("/:id", (ctx, next) => {
  console.log(ctx.params.id);
  ctx.body = "Hello World";
})

1.4.2. 方式二:query

请求地址:http://localhost:8000/login?username=why&password=123

获取query:

app.use((ctx, next) => {
  console.log(ctx.request.query);
  ctx.body = "Hello World";
})

1.4.3. 方式三:json

请求地址:http://localhost:8000/login

body是json格式:

{
    "username": "coderwhy",
    "password": "123"
}

获取json数据:

  • 安装依赖:npm install koa-bodyparser;

  • 使用 koa-bodyparser的中间件;

app.use(bodyParser());

app.use((ctx, next) => {
  console.log(ctx.request.body);
  ctx.body = "Hello World";
})

 

1.4.4. 方式四:x-www-form-urlencoded

请求地址:http://localhost:8000/login

body是x-www-form-urlencoded格式:

x-www-form-urlencoded

获取json数据:(和json是一致的)

  • 安装依赖:npm install koa-bodyparser;

  • 使用 koa-bodyparser的中间件;

app.use(bodyParser());

app.use((ctx, next) => {
  console.log(ctx.request.body);
  ctx.body = "Hello World";
})

1.4.5. 方式五:form-data

请求地址:http://localhost:8000/login

body是form-data格式:

 

解析body中的数据,我们需要使用multer

  • 安装依赖:npm install koa-multer;

  • 使用 multer中间件;

const upload = multer({
});

app.use(upload.any());

app.use((ctx, next) => {
  console.log(ctx.req.body);
  ctx.body = "Hello World";
});

我们知道multer还可以实现文件的上传:

const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "./uploads/")
  },
  filename: (req, file, cb) => {
    cb(null, Date.now() + path.extname(file.originalname))
  }
})

const upload = multer({
  storage
});

const fileRouter = new Router();

fileRouter.post("/upload", upload.single('avatar'), (ctx, next) => {
  console.log(ctx.req.file);
})

app.use(fileRouter.routes());

 

1.5. 响应方式

输出结果:body

将响应主体设置为以下之一:

  • string :字符串数据

  • Buffer :Buffer数据

  • Stream :流数据

  • Object|| Array:对象或者数组

  • null :不输出任何内容

如果response.status尚未设置,Koa会自动将状态设置为200204

比较常见的输出方式:

ctx.response.body = "Hello World";
ctx.body = {
  name: "why",
  age: 18,
  height: 1.88
};
ctx.body = ["abc", "cba", "nba"];

 

疑惑:ctx.response.bodyctx.body之间的区别:

  • 事实上,我们访问ctx.body时,本质上是访问ctx.response.body;

  • 我们可以看到源码中,我们访问 proto(这里就是ctx),其实是访问proto中的response的属性;

image-20201104155927483

请求状态:status

请求状态我们可以直接给ctx设置,或者给ctx.response设置也是一样的效果:

ctx.status = 201;
ctx.response.status = 204;

1.6. 错误处理

const Koa = require('koa');

const app = new Koa();

app.use((ctx, next) => {
  ctx.app.emit('error', new Error("哈哈哈"), ctx);
})

app.on('error', (err, ctx) => {
  console.log(err.message);
  ctx.response.body = "哈哈哈";
})

app.listen(8000, () => {
  console.log("错误处理服务启动成功~");
})

1.7. 静态服务器

koa并没有内置部署相关的功能,所以我们需要使用第三方库:

npm install koa-static

部署的过程类似于express:

const Koa = require('koa');
const static = require('koa-static');

const app = new Koa();

app.use(static('./build'));

app.listen(8000, () => {
  console.log("静态服务器启动成功~");
});

 

在学习了两个框架之后,我们应该已经可以发现koa和express的区别:

从架构设计上来说:

  • express是完整和强大的,其中帮助我们内置了非常多好用的功能;

  • koa是简洁和自由的,它只包含最新的功能,并不会对我们使用其他中间件进行任何的限制。

    • 甚至是在app中连最基本的get、post都没有给我们提供;

    • 我们需要通过自己或者路由来判断请求方式或者其他功能;

因为express和koa框架他们的核心其实都是中间件:

  • 但是他们的中间件事实上,它们的中间件的执行机制是不同的,特别是针对某个中间件中包含异步操作时;

  • 所以,接下来,我们再来研究一下express和koa中间件的执行顺序问题;

我通过一个需求来演示所有的过程:

  • 假如有三个中间件会在一次请求中匹配到,并且按照顺序执行;

  • 我希望最终实现的方案是:

    • 注意:是middleware1中;

    • 在middleware1中,在req.message中添加一个字符串 aaa

    • 在middleware2中,在req.message中添加一个 字符串bbb

    • 在middleware3中,在req.message中添加一个 字符串ccc

    • 当所有内容添加结束后,在middleware1中,通过res返回最终的结果;

2.1. 同步执行顺序

假如我们获取的所有数据,是可以同步获取的;

我们先通过express实现这个过程:

const express = require('express');

const app = express();


const middleware1 = (req, res, next) => {
  req.message = "aaa";
  next();
  res.end(req.message);
}

const middleware2 = (req, res, next) => {
  req.message = req.message + 'bbb';
  next();
}

const middleware3 = (req, res, next) => {
  req.message = req.message + 'ccc';
}

app.use(middleware1, middleware2, middleware3);

app.listen(8000, () => {
  console.log("启动成功~");
})

最终的结果是:aaabbbccc,没问题;

我们再通过koa实现这个过程:

const Koa = require('koa');

const app = new Koa();

const middleware1 = (ctx, next) => {
  ctx.message = "aaa";
  next();
  console.log("aaaa");
  ctx.body = ctx.message;
}

const middleware2 = (ctx, next) => {
  ctx.message = ctx.message + 'bbb';
  console.log("bbbb");
  next();
}

const middleware3 = (ctx, next) => {
  ctx.message = ctx.message + 'ccc';
}

app.use(middleware1);
app.use(middleware2);
app.use(middleware3);

app.listen(8000, () => {
  console.log("启动成功~");
})

最终的结果也是:aaabbbccc,也没问题;

2.2. 异步执行顺序

但是,如果我们最后的ccc中的结果,是需要异步操作才能获取到的,是否会产生问题呢?

2.2.1. express中遇到异步操作

express有异步操作(没有在next前,加async、await):

const middleware1 = (req, res, next) => {
  req.message = "aaa";
  next();
  res.end(req.message);
}

const middleware2 = (req, res, next) => {
  req.message = req.message + 'bbb';
  next();
}

const middleware3 = async (req, res, next) => {
  const result = await axios.get('http://123.207.32.32:9001/lyric?id=167876');
  req.message = req.message + result.data.lrc.lyric;
  console.log(req.message);
}

最终的结果aaabbb,是不正确。

express有异步操作(有在next前,加async、await):

const middleware1 = async (req, res, next) => {
  req.message = "aaa";
  await next();
  res.end(req.message);
}

const middleware2 = async (req, res, next) => {
  req.message = req.message + 'bbb';
  await next();
}

const middleware3 = async (req, res, next) => {
  const result = await axios.get('http://123.207.32.32:9001/lyric?id=167876');
  req.message = req.message + result.data.lrc.lyric;
  console.log(req.message);
}

最终的结果也是aaabbb,也是不正确。

为什么呢?

  • 原因是本质上的next()和异步没有任何关系;

  • 它本身就是一个同步函数的调用,所以它不会等到你异步有结果之后,再继续执行后续的操作;

image-20201106175205300

2.2.2. koa中遇到异步操作

koa有异步操作(没有在next前,加async、await):

const middleware1 = async (ctx, next) => {
  ctx.message = "aaa";
  next();
  ctx.body = ctx.message;
}

const middleware2 = async (ctx, next) => {
  ctx.message = ctx.message + 'bbb';
  next();
}

const middleware3 = async (ctx, next) => {
  const result = await axios.get('http://123.207.32.32:9001/lyric?id=167876');
  ctx.message = ctx.message + result.data.lrc.lyric;
}

最终的结果也是aaabbb,也是不正确。

  • 这是因为虽然next函数是一个返回promise的异步操作,但是在前面不加await的情况,是不同等待结果的返回,就会继续向后执行了;

koa有异步操作(有在next前,加async、await):

const middleware1 = async (ctx, next) => {
  ctx.message = "aaa";
  await next();
  ctx.body = ctx.message;
}

const middleware2 = async (ctx, next) => {
  ctx.message = ctx.message + 'bbb';
  await next();
}

const middleware3 = async (ctx, next) => {
  const result = await axios.get('http://123.207.32.32:9001/lyric?id=167876');
  ctx.message = ctx.message + result.data.lrc.lyric;
}

最终的结果是aaabbb+歌词信息,是正确。

  • 这是因为,当我们在koa中的next前面加await时,它会等到后续有一个确定结果时,在执行后续的代码;

 

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

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

相关文章

【云计算与大数据技术】Google、亚马逊、IBM、阿里云等云计算应用平台介绍讲解(超详细)

云应用跟云计算最大的不同在于,云计算作为一种宏观技术发展概念而存在,而云应用则是直接面对客户解决实际问题的产品。 “云应用”的工作原理是把传统软件“本地安装、本地运算”的使用方式变为”即取即用”的服务,通过互联网或局域网连接并操…

VUE学习(五)VUE项目发布启动

在项目目录下运行 npm run build 2.下载nginx nginx: download 双击nginx.exe运行 3.将打包好的vue项目下dist目录所有文件拷贝 4.粘贴至nginx目录html下 5.此时在浏览器中输入localhost 即可看到发布的网站了

【云计算与大数据技术】大数据概念和发展背景讲解(图文解释 超详细)

一、什么是大数据 大数据是一个不断发展的概念,可以指任何体量或负载下那个超出常规数据处理方法和处理能力的数据,数据本身可以是结构化,半结构化甚至是非结构化的,随着物联网技术与可穿戴设备的飞速发展,数据规模变…

首篇!BEV-Locator:多目端到端视觉语义定位网络(清华轻舟智航)

点击下方卡片,关注“自动驾驶之心”公众号ADAS巨卷干货,即可获取点击进入→自动驾驶之心【SLAM】技术交流群后台回复【BEV定位】获取本文论文!摘要准确的定位能力是自动驾驶的基础。传统的视觉定位框架通过几何模型来解决语义地图匹配问题&am…

【C语言经典面试题】这样的char * 定义怎么回事

作者简介 *架构师李肯(全网同名)**,一个专注于嵌入式IoT领域的架构师。有着近10年的嵌入式一线开发经验,深耕IoT领域多年,熟知IoT领域的业务发展,深度掌握IoT领域的相关技术栈,包括但不限于主流…

Cookie | Cookie的理论基础、Cookie中常用的方法

目录 一:Cookie的理论基础 二:Cookie中常用的方法 一:Cookie的理论基础 (1)cookie怎么生成? ①session的实现原理中,每一个session对象都会关联一个sessionid,例如:JS…

中创软件递交上会稿:年营收不到2亿 税收优惠占利润比高

雷递网 雷建平 12月10日山东中创软件商用中间件股份有限公司(简称:“中创软件”)日前递交上会稿,准备在科创板上市。中创软件计划募资6亿元,其中,2.3亿元用于应用基础设施及中间件研发项目,1.3亿…

开源一个各种USB电缆的测试仪,再也不用担心被只有充电功能的数据线坑了

作者:晓宇,排版:晓宇微信公众号:芯片之家(ID:chiphome-dy)01 想知道你的USB Type C数据线是USB2或者USB3吗?大家是否有一些仅有充电功能的USB数据线在你的抽屉里,等待某个…

量子计算(十三):量子计算的if和while

文章目录 量子计算的if和while 一、基于测量的跳转 二、基于量子信息的IF和WHILE 量子计算的if和while 所谓量子线路,从本质上是一个量子逻辑门的执行序列,它是从左至右依次执行的。即使介绍了函数调用的思想,也可以理解为这是一种简单地…

10-11-12 - 实模式到保护模式

---- 整理自狄泰软件唐佐林老师课程 文章目录1. 从计算机的历史谈起2. 绝对的权利带来的问题3. CPU历史的里程碑 - 80863.1 深入解析 [段地址 : 偏移地址]3.1.1 示例3.1.2 问题3.2 8086时期应用程序中的问题3.3 思考4. 80286的登场4.1 80286的兼容性4.2 初识保护模式4.2.1 描述…

JDBC连接数据库详解~

课前导读: 本文章有关JDBC连接数据库的操作,其中涉及了编译器idea的使用,其中连接的数据库为MySQL数据库,通过博主自己的学习和总结写出了下面的文章。 目录 课前导读: 一、JDBC简介 二、JDBC连接操作 三、源码提供…

数素数

目录 1013 数素数 输入格式: 输出格式: 输入样例: 输出样例: 代码长度限制: 时间限制: 内存限制: 思路: 1.判断素数 1.2素数判断代码: 2.数组存素数 2.2存素数代码: 3.输出Pm到Pn的全部素数 3.2代码 总代码: 总结: 题目…

Web大学生网页作业成品 bootstrap响应式网站开发 基于HTML+CSS+JS+Bootstrap制作火锅美食网站(4页)

🎀 精彩专栏推荐👇🏻👇🏻👇🏻 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业…

非零基础自学计算机操作系统 第1章 操作系统概述 1.4 操作系统的分类 1.4.3 实时操作系统 1.4.4 通用操作系统 1.4.5 单用户操作系统

非零基础自学计算机操作系统 文章目录非零基础自学计算机操作系统第1章 操作系统概述1.4 操作系统的分类1.4.3 实时操作系统1.4.4 通用操作系统1.4.5 单用户操作系统第1章 操作系统概述 1.4 操作系统的分类 1.4.3 实时操作系统 所谓实时,是指系统能够对外部请求做…

R语言多重比较方法

假设检验的基本原理是小概率原理,即我们认为小概率事件在一次试验中实际上不可能发生。 多重比较的问题 当同一研究问题下进行多次假设检验时,不再符合小概率原理所说的“一次试验”。如果在该研究问题下只要有检验是阳性的,就对该问题下阳性…

c#入门-静态引用,扩展方法

静态引用 声明静态引用 引用命名空间时,可以在using后面加static修饰符。 然后把要引用的命名空间改为带命名空间的一个具体的类。 这称为静态引用。 using static System.Int32; using static System.Console; int a Parse(ReadLine());静态引用的效果 静态引…

VRC改模从入门到入门

第一步入门 从unity开始 bilibili超细心的unity入门教程 虽然视频很长,但是从p1看到p41就行,脚本部分完全不需要看,因为vrc上传会过滤掉不是他们的脚本的。 unity下载一定要下载2019.4.31f1版本,哔哩哔哩教程有下载unity对应版本…

基于Android的外卖App系统设计

基于Android的外卖App系统设计 摘要 随着智能手机的快速普及,智能手机操作系统市场风生水起。为了让智能手机用户能够随时随地查询互联网所提供的服务,一种高效的办法就是将应用系统的功能拓展到手机终端上,让手机能够通过移动网以及互联网…

CentOS 安装 mysql 8.0

目录 下载 安装步骤 上传 解压安装 参考资料 补充报错解决 下载 MySQL :: Download MySQL Community Serverhttps://dev.mysql.com/downloads/mysql/ 安装步骤 上传 使用xshell,连接服务器,用 rz命令接收下载的文件(该命令使用 yum…

Linux 文件属性

Linux 系统是一种典型的多用户系统,不同的用户处于不同的地位,拥有不同的权限。 为了保护系统的安全性,Linux 系统对不同的用户访问同一文件(包括目录文件)的权限做了不同的规定。 在 Linux 中我们通常使用以下两个命…