nodejs之web服务器框架koa


1. koa

Koa应用于node.js的下一代web框架
Koa它使得中间件能够按照特定的顺序执行,同时支持异步操作。这对于实现诸如日志记录、身份验证、请求处理、响应处理等功能非常有用。

koa和express是同一个团队开发的

  • koa旨在为web应用程序和api提供更小,更丰富和更强大的能力
  • 相对于express具有更强的异步出来能力
  • koa的核心代码只有1600+行,是一个轻量级的web开发框架,可以根据需要安装和使用中间件

1.Koa简介

Koa,作为Node.js平台上一个轻量级的web开发框架,以其独特的中间件流程和洋葱模型而闻名。这个模型不仅简化了异步编程的复杂性,还提供了灵活的中间件管理机制,使得开发者能够轻松构建可扩展的web应用。

Koa是一个由Express幕后团队打造的新web框架,它致力于成为更小、更富有表现力、更健壮的基石。Koa并没有捆绑任何中间件,而是提供了一套优雅的方法,帮助开发者快速编写服务端应用程序。其核心特性包括将Node.js原生的请求(req)和响应(res)对象封装为上下文(context)对象,以及基于async/await的中间件洋葱模型机制。

2.Koa工作原理

洋葱模型的核心原理是借助compose方法。

  • 当执行app.listen()监听端口时,Koa内部会调用http模块的createServer方法,并传入一个内置的回调函数。
  • 回调函数将使用compose函数来处理中间件数组。
  • compose函数通过递归调用中间件,实现了洋葱模型的执行流程。

2.Koa源码分析

compose函数的实现是洋葱模型的核心。它接收一个中间件数组,并返回一个函数,该函数接收上下文(context)和next函数作为参数。在compose函数内部,通过递归调用dispatch函数来实现中间件的执行流程。每当next()被调用时,dispatch函数会递增索引并继续调用下一个中间件。

2. Koa使用

1.Koa的安装

Koa 需要 node v12 或更高版本才能支持 ES2015 和异步函数。

// 安装指令
 npm i koa
 // 服务启动指令 服务文件名 koaApp.js
 node ./koaApp.js

2.Koa的应用程序

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

app.use(async ctx => {
  ctx.body = 'Hello World';
});

app.listen(8081, function() {
  console.log('服务启动!')
});

3.Koa的属性

属性/方法名默认值说明
app.envNODE_ENV 或 development项目环境
app.keys设置签名的 Cookie 密钥
app.proxyfalse当真正的代理头字段将被信任时
app.subdomainOffset2偏移量
app.proxyIpHeaderX-Forwarded-For代理 ip 消息头
app.maxIpsCount从代理 ip 消息头读取的最大 ip限制数, 默认为 0 (代表无限)
app.listen()启动服务器并监听特定端口
app.callback()返回适用于 http.createServer() 方法的回调函数来处理请求
app.use()将给定的中间件方法添加到此应用程序
app.context为 ctx 添加其他属性

打印 new Koa()
在这里插入图片描述

  1. app.keys
    需要将signed选项设置为true,这样Koa才会使用app.keys中的密钥进行加密。
    const Koa = require('koa');
    const app = new Koa();
    app.keys = ['im a newer secret', 'i like turtle'];
    app.use(async ctx => {
      ctx.cookies.set('name', 'tobi', { signed: true });
      //一个名为name的普通cookie和一个名为name.sig的签名cookie。
      ctx.body = 'Hello World';
    });
    app.listen(8081, function() {
      console.log('服务启动!')
    });
    
    在这里插入图片描述
  2. app.proxy
    const Koa = require('koa');
    const app = new Koa({ proxy: true });
    
    const Koa = require('koa');
    const app = new Koa();
    app.proxy = true;
    
    在这里插入图片描述
  3. app.listen()
    Koa 应用程序不是 HTTP 服务器的1对1展现。 可以将一个或多个 Koa 应用程序安装在一起以形成具有单个HTTP服务器的更大应用程序。
    app.listen(…) 方法只是以下http.createServer(app.callback())方法的语法糖
    const Koa = require('koa');
    const app = new Koa();
    const http = require('http');
    app.use(async ctx => {
      ctx.body = 'Hello World';
    });
    http.createServer(app.callback()).listen(8081, function() {
      console.log('服务启动!')
    });
    
    可以将同一个应用程序同时作为 HTTP 和 HTTPS 或多个地址:
    const Koa = require('koa');
    const app = new Koa();
    const http = require('http');
    app.use(async ctx => {
      ctx.body = 'Hello World';
    });
    http.createServer(app.callback()).listen(8081, function() {
      console.log('服务启动!端口号:8081')
    });
    http.createServer(app.callback()).listen(3001, function() {
      console.log('服务启动!端口号:3001')
    });
    
    在这里插入图片描述
    在这里插入图片描述
  4. app.callback()
    返回适用于 http.createServer() 方法的回调函数来处理请求。
  5. app.use()
    将给定的中间件方法添加到此应用程序。app.use() 返回 this, 因此可以链式表达.
    app.use(someMiddleware)
      .use(someOtherMiddleware)
      .listen(3000)
    
  6. app.context
    app.context 是从其创建 ctx 的原型,可以通过编辑 app.context 为 ctx 添加其他属性

    注:

    1. ctx 上的许多属性都是使用 getter ,setter 和 Object.defineProperty() 定义的。你只能通过在 app.context 上使用 Object.defineProperty() 来编辑这些属性(不推荐)。
    2. 安装的应用程序目前使用其父级的 ctx 和设置。 因此,安装的应用程序只是一组中间件。

4.Koa错误处理

添加一个 “error” 事件侦听器进行错误处理。

app.on('error', (err, ctx) => {
  consolog.error('server error', err)
  ctx.status = 401;
  ctx.body = err.message;
});

5.Koa的上下文Context

Koa Context 将 node 的 request 和 response 对象封装到单个对象中,为编写 Web 应用程序和 API 提供了许多有用的方法。
Context 具体方法和访问器

方法/访问器说明
ctx.reqNode 的 request 对象.
ctx.resNode 的 response 对象
ctx.requestKoa的 request 对象.
ctx.responseKoa的 response 对象
ctx.state推荐的命名空间,用于通过中间件传递信息和你的前端视图。
ctx.app应用程序实例引用
ctx.app.emitctx.app.emit 发出一个类型由第一个参数定义的事件。对于每个事件,您可以连接 “listeners”,这是在发出事件时调用的函数
ctx.cookies.get(name, [options])通过 options 获取 cookie name
ctx.cookies.set(name, value, [options])通过 options 设置 cookie name 的 value
ctx.throw([status], [msg], [properties])用来抛出一个包含 .status 属性错误的帮助方法,
ctx.assert(value, [status], [msg], [properties])当 !value 时抛出一个类似 .throw 错误的帮助方法
ctx.respond想要写入原始的 res 对象而不是让 Koa 处理你的 response,请使用此参数
  1. ctx.cookies.get(name, [options])
    从请求的标头中提取具有给定名称的 Cookie。如果存在此类 Cookie,则返回其值。否则,不会返回任何内容。Cookie
    { signed: true }可以选择作为第二个参数 options 传递。在这种情况下,将获取签名 Cookie(以附加后缀结尾的同名 Cookie)。如果不存在此类 Cookie,则不会返回任何内容。.sig

  2. ctx.cookies.set(name, value, [options])

    const Koa = require('koa');
    const KeyGrip = require('keygrip');
    const app = new Koa();
    app.use(async ctx => {
      app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
      ctx.cookies.set(
        'cid', 
        '11111111',
        {
          domain: 'localhost:8081',  // 写cookie所在的域名
          path: '/',       // 写cookie所在的路径
          maxAge: 10 * 60 * 1000, // cookie有效时长
          expires: new Date('2025-06-15'),  // cookie失效时间
          httpOnly: true,  // 是否只用于http请求中获取
          overwrite: false,  // 是否允许重写,默认false
          signed: true, // 一个布尔值, 表示是否要对 cookie 进行签名 (默认为 false). 如果为 true, 则还会发送另一个后缀为 .sig 的同名 cookie, 
          secure: false, // cookie 是否仅通过 HTTPS 发送 (HTTP 下默认为 false, HTTPS 下默认为 true)
          sameSite: false,//一个布尔值或字符串, 表示该 cookie 是否为 "相同站点" cookie (默认为 false). 可以设置为 'strict', 'lax', 'none', 或 true (映射为 'strict').
        }
      )
      const cid = ctx.cookies.get('cid', { signed: true })
      console.log(cid)
      ctx.body = 'Hello World';
    });
    app.listen(8081, function() {
      console.log('服务启动!端口号:8081')
    });
    

    在这里插入图片描述
    在这里插入图片描述

  3. ctx.throw([status], [msg], [properties])
    用来抛出一个包含 .status 属性错误的帮助方法,其默认值为 500

     const user = {
        id: 123,
        name: 'Alice',
        email: 'alice@example.com'
      }
      ctx.throw(400, 'name required', { user: user });
    

    ctx.throw(400, 'name required', { user: user });等效于

    const err = new Error('name required');
    err.status = 400;
    err.expose = true;
    throw err;
    

    请求参数和响应参数属性可查看官网

3. koa-router路由

Koa框架并没有直接提供app.get, app.put, app.post这样的方法来定义路由。在Koa中,我们使用koa-router这个中间件来实现路由的功能。

1. koa-router的使用

  1. 安装指令
    npm install koa-router
    
  2. 项目中使用
    const Koa = require('koa');
    const app = new Koa();
    // 引入Router
    var Router = require('koa-router');
    // 实例化 Router
    var router = new Router();
    // 定义路由
    router.get('/', (ctx, next) => {
      ctx.body = 'Hello World!';
    });
    app.listen(8081, function() {
      console.log('服务启动!端口号:8081')
    });
    
    在这里插入图片描述
    在这里插入图片描述
  3. 设置路径前缀
    实例化时传参实现
    var router = new Router({
      prefix: '/api'
    });
    
    实例化之后实现
    	var router = new Router();
    	router.prefix('/api')
    
  4. allowedMethods方法
    单独的中间件,用于响应 包含允许的方法的标头。
    options参数简介
    属性类型说明
    throwBooleanthrow 错误
    notImplementedfunctionthrow 返回值来代替默认的 NotImplemented 错误
    methodNotAllowedfunction抛出返回值来代替默认的 MethodNotAllowed 错误

2. koa-router的参数解析

  1. 参数解析:params和query
    const Koa = require('koa');
    const app = new Koa();
    // 引入Router
    var Router = require('koa-router');
    var router = new Router();
    router.get('/login_get/:id', (ctx, next) => {
       let str = JSON.stringify(ctx.request.params)
      let str1 = JSON.stringify(ctx.request.query)
      ctx.body = `params: ${str}; query: ${str1}` ;
    });
    app
      .use(router.routes())
      .use(router.allowedMethods());
    
    app.listen(8081, function() {
      console.log('服务启动!端口号:8081')
    });
    
    在这里插入图片描述
  2. 参数解析:query
    const Koa = require('koa');
    	const app = new Koa();
    	// 引入Router
    	var Router = require('koa-router');
    	var router = new Router();
    	router.get('/login', (ctx, next) => {
    	  let str = JSON.stringify(ctx.request.query)
    	  ctx.body = str;
    	});
    	app
    	  .use(router.routes())
    	  .use(router.allowedMethods());
    	
    	app.listen(8081, function() {
    	  console.log('服务启动!端口号:8081')
    	});
    
    在这里插入图片描述
  3. 参数解析:json和formData
    const Koa = require('koa');
    const app = new Koa();
    const bodyParser = require('koa-bodyparser');
    const multer = require('koa-multer');
    
    const upload = multer();
    app.use(bodyParser());
    app.use(upload.any());
    // 引入Router
    var Router = require('koa-router');
    // 实例化 Router
    var router = new Router();
    // 定义路由
    router.post('/login_POST', (ctx, next) => {
      let str = JSON.stringify(ctx.request.body)
      ctx.body = str;
    });
    router.post('/login_post_json', (ctx, next) => {
      let str = JSON.stringify(ctx.request.body)
      ctx.body = str;
    });
    // 使用路由中间件
    app
      .use(router.routes())
      .use(router.allowedMethods());
    
    app.listen(8081, function() {
      console.log('服务启动!端口号:8081')
    });
    
    在这里插入图片描述
    在这里插入图片描述

3. koa上传文件

使用koa-multer中间件来处理文件上传。koa-multer是multer的一个为Koa提供的中间件,而multer是一个用于处理multipart/form-data类型数据(主要是文件上传)的中间件,它适用于Express,也可以在Koa中使用。

const Koa = require('koa');
const app = new Koa();
const multer = require('koa-multer');
const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    cb(null, '/uploads') // 确保这个文件夹已经存在,物理路径
  },
  filename: function (req, file, cb) {
    cb(null, file.fieldname + '-' + Date.now())
  }
})
const upload = multer({ storage: storage });
var Router = require('koa-router');
var router = new Router();
router.post('/upload', upload.single('file'), (ctx, next) => {
  console.log(ctx.req.file)
  ctx.body = 'str';
});
// 使用路由中间件
app
  .use(router.routes())
  .use(router.allowedMethods());

app.listen(8081, function() {
  console.log('服务启动!端口号:8081')
});
   <form action="http://127.0.0.1:8081/upload" method="post" enctype="multipart/form-data">
      <input type="file" name="file" />  <br>
      <input type="submit" value="Submit" />
    </form>

在这里插入图片描述
在这里插入图片描述

4. 嵌套路由

路由对象上又继续构建其他子路由对象

var router = new Router(); // 总路由
let userRouter = new Router();//创建一个用户路由对象
let classRou = new Router();//创建一个企业用户路由对象
classRou.get('/show',async ctx=>{
    	ctx.body = '班级';//企业用户方法
})

let admin = new Router();//创建个人用户路由对象
admin.get('/show',async ctx=>{
  ctx.body = "个人";//个人路由对象方法
})

userRouter.use('/classInfo',classRou.routes());
userRouter.use('/admin',admin.routes());

router.use('/user', userRouter.routes());
// 使用路由中间件
app
  .use(router.routes())
  .use(router.allowedMethods());

app.listen(8081, function() {
  console.log('服务启动!端口号:8081')
});

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程楠楠&M

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值