切换语言为:繁体

聊聊 Koa 框架的基本使用方法

  • 爱糖宝
  • 2024-07-09
  • 2073
  • 0
  • 0

前言

如果不使用koa,用原生的node来创建一个简单的 HTTP 服务器,并定义端口号为3000

const http = require('http')

const server = http.createServer((req, res) => {
    res.end('hello world')
})

server.listen(3000, () => {
    console.log('server is running on 3000 port');
})


聊聊 Koa 框架的基本使用方法

接下来我们用对此进行二次封装的KOA来创建这个demo

KOA

“Koa”通常指的是 Koa 框架,它是一个基于 Node.js 的 Web 应用框架,由 Express 原班人马打造。

Koa 致力于成为一个更小、更富有表现力、更健壮的 Web 框架。它使用 async/await 语法来处理异步流程,使得代码更加简洁和易读。

Koa 具有轻量、灵活、中间件机制等特点,能够帮助开发者更高效地构建 Node.js 应用。

首先安装依赖

npm i koa


小试牛刀

const Koa = require('koa');

const app = new Koa();

app.listen(3000, () => {
    console.log('server is running at port 3000');
});


能够看出来其实这就是对刚刚的代码做了一个封装。接下来我们创建一个函数体,然后用app给use掉,携带koa的上下文对象context,事实上他集成了我们最初始的res和req两个对象。

const Koa = require('koa');

const app = new Koa();

const main = (ctx) => {
    console.log(ctx);
}
app.use(main);


app.listen(3000, () => {
    console.log('server is running at port 3000');
});


  1. const main = (ctx) => { console.log(ctx); } :定义了一个名为 main 的中间件函数,它接收一个 ctx 参数,即上下文对象,并将其打印到控制台。

  2. app.use(main); :使用 app.use 方法注册中间件函数 main,使其在每次请求处理时被调用。

ctx

ctx 是 Koa 中的上下文(Context)对象,它封装了 Node.js 中的原生 req(请求)和 res(响应)对象。可以将 ctx 看做是一次 HTTP 请求和响应过程中的相关信息的集合。

通过 ctx,开发者可以方便地访问和操作请求及响应的各种属性和方法。

ctx 对象具有以下主要属性:

  • ctx.req:原生的 req 对象。

  • ctx.res:原生的 res 对象。

  • ctx.request:Koa 自己封装的请求对象,该对象不仅包含原生 req 对象的属性,还有一些额外的便捷方法和属性。例如,可以更方便地获取查询参数(ctx.query )、解析 URL(ctx.path )等。

  • ctx.response:Koa 自己封装的响应对象,同样具有一些额外的方法和属性,方便设置响应的相关信息。

  • ctx 本身还代理了 ctx.request 和 ctx.response 身上的属性,这意味着可以直接通过 ctx 来访问 ctx.request 和 ctx.response 的部分属性,例如直接使用 ctx.query 来获取查询参数,而无需使用 ctx.request.query

我们把ctx打印出来是这样的:

{
  request: {
    method: 'GET',
    url: '/',
    header: {
      host: 'localhost:3000',
      connection: 'keep-alive',
      'cache-control': 'max-age=0',
      'sec-ch-ua': '"Not/A)Brand";v="8", "Chromium";v="126", "Microsoft Edge";v="126"',
      'sec-ch-ua-mobile': '?0',
      'sec-ch-ua-platform': '"Windows"',
      'upgrade-insecure-requests': '1',
      'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36 Edg/126.0.0.0',
      accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
      'sec-fetch-site': 'none',
      'sec-fetch-mode': 'navigate',
      'sec-fetch-user': '?1',
      'sec-fetch-dest': 'document',
      'accept-encoding': 'gzip, deflate, br, zstd',
      'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6'
    }
  },
  response: {
    status: 404,
    message: 'Not Found',
    header: [Object: null prototype] {}
  },
  app: { subdomainOffset: 2, proxy: false, env: 'development' },
  originalUrl: '/',
  req: '<original node req>',
  res: '<original node res>',
  socket: '<original node socket>'
}


因此此时我们想向前端响应一个hello world,甚至可以像最开始那样,直接使用他封装好了的ctx里面的res.end。

const main = (ctx) => {
    // console.log(ctx);
    ctx.res.end('hello koa');
}


除此之外,我们还可以给response响应体上挂一个body属性

const main = (ctx) => {
    // console.log(ctx);
    // ctx.res.end('hello koa');
    ctx.response.body = 'hello koa';
}
app.use(main);


但是这么写属实是比原生更加麻烦了,因此koa做了一个代理,允许我们直接省略response

const main = (ctx) => {
    // console.log(ctx);
    // ctx.res.end('hello koa');
    ctx.body = 'hello koa';
}
app.use(main);`js


同理,想获取url,就可以直接使用原生的req.url也可以用封装好的,再直接可以省略。

const main = (ctx) => {
    // console.log(ctx);
    // ctx.res.end('hello koa');
    // ctx.response.body = 'hello koa';
    ctx.body = 'hello koa';
    console.log(ctx.req.url);
    console.log(ctx.request.url);
    console.log(ctx.url);
}
app.use(main);


不由感叹,开源的力量,不用大家重复造轮子,这些封装都很优雅,支持各种写法,帮助大家省了很多麻烦。

我们来看看,koa还封装了个accepts

const main = (ctx) => {
    if (ctx.request.header.accept === 'xml') {
        ctx.body = '<data>hello xml </data>';
    } else if (ctx.request.accepts('html')) {
        ctx.body = '<p>hello html</p>';
    }
}
app.use(main);


我们发现原生的拿accept无法匹配到,除非把请求头全部拿过来(text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.7)

为此koa封装了ctx.request.accepts方法,可以直接匹配。

如果我们想读取一个html文件然后给他响应到浏览器上

const fs = require('fs');

const Koa = require('koa');
const app = new Koa();
const main = (ctx) => {
    ctx.response.type = 'text/html';
    const context = fs.readFileSync('./template.html', 'utf-8') //toString()
    console.log(context);
    ctx.body = context;

}



app.use(main);
app.listen(3000, () => {
    console.log('server is running at port 3000');
});


  • 要让浏览器读得懂,要设置响应头,告诉浏览器这是一个html,如果没有toString或者没有设置utf8编码,那么会拿到一个buffer流,如果响应一个buffer流就会自动触发下载,因此此时让你去做一个触发下载某个东西的操作你也可以做了。

  • 我们能够理解,这里ctx.response.type可以直接省略response了。

  • 接下来再换一种方式来写

const fs = require('fs');

const Koa = require('koa');
const app = new Koa();
const main = (ctx) => {
    // ctx.response.type = 'text/html';
    // const context = fs.readFileSync('./template.html', 'utf-8') //toString()
    // console.log(context);
    // ctx.body = context;

    // ctx.response.type = 'text/html';
    ctx.res.writeHead(200,{'Content-type':'text/html'}) // 原生的方式不爱用就用上面的
    const content = fs.createReadStream('./template.html');
    console.log(content);
    ctx.body = content;
}



app.use(main);
app.listen(3000, () => {
    console.log('server is running at port 3000');
});


koa路由

用最开始的办法写路由

const fs = require('fs');

const Koa = require('koa');
const app = new Koa();
const main = (ctx) => {
    if (ctx.url === '/') {
        ctx.type = 'text/html';
        ctx.body = '<h2>home</h2>'
    }else{
        ctx.type = 'text/html';
        ctx.body = '<a href="/">go home</a>'
    }
}


可以看出,这种写法也是相当的恶心。因此我们使用koa-route,因为这个框架非常简单,因此很多团队都可以自己做封装,大家可以自行选择使用什么koa-route/koa-router...

安装依赖

npm i koa-route


小试牛刀

const router = require('koa-route');
const Koa = require('koa');
const app = new Koa();
const main = (ctx) => {
    ctx.type = 'html';
    ctx.body = '<h2>home</h2>'

}
const about = (ctx) => {
    ctx.type = 'html';
    ctx.body = '<a href="/">about,go home</a>'
}


app.use(router.get('/', main))
app.use(router.get('/about', about))
// app.use(main);
app.listen(3000, () => {
    console.log('server is running at port 3000');
});


注意:事实上koa只能use一个,这里的路由能use两个是因为封装过了,此时我们自己写一个打印日志方法如果不写next,后面的use就无法使用,因此:

const router = require('koa-route');
const Koa = require('koa');
const app = new Koa();
const main = (ctx) => {
    ctx.type = 'html';
    ctx.body = '<h2>home</h2>'

}
const about = (ctx) => {
    ctx.type = 'html';
    ctx.body = '<a href="/">about,go home</a>'
}
const logger = (ctx, next) => {
    console.log(`${ctx.method} - ${ctx.url} - ${Date.now()}`);
    next();
}

app.use(logger)
app.use(router.get('/', main))
app.use(router.get('/about', about))

// app.use(main);
app.listen(3000, () => {
    console.log('server is running at port 3000');
});


next

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

const one = (next) =>{ // 中间件
    console.log(1);
    console.log(2);
}

const two = () => {
    console.log(3);
    console.log(4);
}

const three = () => {
    console.log(5);
    console.log(6);
}

app.use(one);
app.use(two);
app.use(three); 

app.listen(3000, () => {
    console.log('server is running at port 3000');
});


如果是这样一份代码,势必只会打印1,2,因此要通过next的调用进入下一个中间件,一碰到next就会进入下一个中间件,如果走完了,就会回头走没走完的。

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

const one = (next) =>{ // 中间件
    console.log(1);
    next()
    console.log(2);
}

const two = (next) => {
    console.log(3);
    next()
    console.log(4);
}

const three = () => {
    console.log(5);
    console.log(6);
}

app.use(one);
app.use(two);
app.use(three); 

app.listen(3000, () => {
    console.log('server is running at port 3000');
});


因此,答案势必是:1,3,5,6,4,2,看得出来,这底层一定是用递归写的。不难理解,koa的中间件执行过程就是个洋葱模型,用一根筷子插入一个洋葱,进入第一层,第二层,第三层,然后到了中间,继续深入就回到了第二层,第一层。

小结

“Koa”是一个基于 Node.js 的 Web 应用框架,它以简洁、高效和灵活著称。

本文介绍了一下Koa的基本使用方法,以及其中的一些细节内容,ctx包括什么东西,next怎么使用,如何使用koa做路由...

Koa主要特点包括:

  1. 基于 async/await :使用 ES6 的异步函数语法,让异步流程的控制更加直观和简洁。

  2. 轻量级:核心模块非常小,只包含最基本的功能,开发者可以根据需求选择和添加扩展。

  3. 中间件机制:通过 app.use 方法来添加中间件,中间件可以按照添加的顺序依次执行,方便对请求和响应进行处理和修改。

0条评论

您的电子邮件等信息不会被公开,以下所有项均必填

OK! You can skip this field.