添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
app . listen ( 3000 , ( ) => { console . log ( 'server is running on http://localhost:3000' )

nodemon ./app.js 启动服务。

源码目录结构

查看 node_modules/koa package.json ,查看加载 koa 时实际加载的文件:

"main": "lib/application.js",
// node_modules\koa\lib\application.js
module.exports = class Application extends Emitter {
  listen(...args) {
    debug('listen');
    // 使用原生 http 模块开启 HTTP 服务,成功后调用回调
    const server = http.createServer(this.callback());
    return server.listen(...args);
  callback() {
    // 获取全部中间件并依次调用
    const fn = compose(this.middleware);
    if (!this.listenerCount('error')) this.on('error', this.onerror);
    const handleRequest = (req, res) => {
      const ctx = this.createContext(req, res);
      return this.handleRequest(ctx, fn);
    return handleRequest;
  // 挂载中间件的方法
  use(fn) {...}

Koa 中大量使用了 ES6 的语法(例如 class)进行编写,加载 koa,实际上加载的是 Application 类,通过实例化这个类创建 app

lib 下其他文件:

├─ application.js # 负责整个应用的创建组织
├─ context.js # 处理 context 上下文对象
├─ request.js # 内部封装的 context.request 对象
└─ response.js # 内部封装的 context.response 对象

下面开始模仿一个极简的 Koa。

在项目目录下新建一个 my-koa 文件夹存放模拟代码。

mkdir my-koa
cd ./my-koa
# 初始化 npm 以在该目录下安装依赖
npm init -y

修改入口文件路径:

// my-koa/package.json
"main": "lib/application.js",

添加文件:

// my-koa\lib\application.js
const http = require('http')
class Application {
  listen(...args) {
    const server = http.createServer((req, res) => {
      res.end('My Koa')
    server.listen(...args)
module.exports = Application

重新访问 http://localhost:3000 测试结果。

const one = (ctx, next) => {
  console.log('>> one')
  next()
  console.log('<< one')
const two = (ctx, next) => {
  console.log('>> two')
  // next()
  console.log('<< two')
const three = (ctx, next) => {
  console.log('>> three')
  next()
  console.log('<< three')
app.use(one)
app.use(two)
app.use(three)
console.log(app.middleware)
// my-koa\lib\application.js
const http = require('http')
class Application {
  constructor() {
    // 保存用户添加的中间件函数
    this.middleware = []
  listen(...args) {
    const server = http.createServer(this.callback())
    server.listen(...args)
  use(fn) {
    this.middleware.push(fn)
  // 异步递归遍历调用中间件处理函数
  compose(middleware) {
    // 返回一个高级函数,允许接受其他参数
    return function () {
      const dispatch = index => {
        if (index >= middleware.length) {
          return Promise.resolve()
        const fn = middleware[index]
        // 将中间件函数调用包装为一个 Promise 兼容异步处理
        return Promise.resolve(
          // fn(ctx, next)
          fn({}, () => dispatch(index + 1))
      // 返回并调用第一个中间件处理函数
      return dispatch(0)
  callback() {
    // 获取调用第一个中间件函数的方法
    const fnMiddleware = this.compose(this.middleware)
    const handleRequest = (req, res) => {
      // 开始执行第一个中间件函数
      fnMiddleware().then(() => {
        console.log('end')
        res.end('My Koa')
      }).catch(err => {
        console.log(err.message)
    return handleRequest
module.exports = Application

分析 context 对象的内容组成

Koa Context 实际上是将 node 的 request 和 response 对象封装到单个对象中,为编写 Web 应用程序和 API 提供了许多有用的方法。

每一个请求都将创建一个 Context,并在中间件中作为参数引用。

// 打印 Koa Context:
  request: {
    method: 'GET',
    url: '/',
    header: {...}
  }, // Koa 封装的 request 对象
  response: {
    status: 404,
    message: 'Not Found',
    header: {...}
  }, // Koa 封装的 response 对象
  app: { ... }, // app 实例
  originalUrl: '/',
  req: '<original node req>', // node 原生 request 对象
  res: '<original node res>', // node 原生 response 对象
  socket: '<original node socket>' //  // node 原生 socket 对象
app.use((ctx, next) => {
  console.log(ctx)
  // node 原生对象
  console.log(ctx.req)
  console.log(ctx.res)
  console.log(ctx.req.url)
  // Koa 封装的 Request 对象
  console.log(ctx.request)
  console.log(ctx.request.header)
  console.log(ctx.request.method)
  console.log(ctx.request.url)
  console.log(ctx.request.path)
  console.log(ctx.request.query)
  // Request 别名
  console.log(ctx.header)
  console.log(ctx.method)
  console.log(ctx.url)
  console.log(ctx.path)
  console.log(ctx.query)
  // Koa 封装的 Response 对象
  console.log(ctx.response)
  // ctx.response.status = 200
  // ctx.response.message = 'Success'
  // ctx.response.type = 'plain'
  // ctx.response.body = 'Hello Koa'
  // Response 别名
  ctx.status = 200
  ctx.message = 'Success'

初始化 Context 上下文对象

// my-koa\lib\application.js
const http = require('http')
const context = require('./context')
const request = require('./request')
const response = require('./response')
class Application {
  constructor() {
    // 保存用户添加的中间件函数
    this.middleware = []
    // 拷贝创建,避免互相污染
    this.context = Object.create(context)
    this.request = Object.create(request)
    this.response = Object.create(response)
  listen(...args) {...}
  use(fn) {...}
  // 异步递归遍历调用中间件处理函数
  compose(middleware) {
    // 返回一个高级函数,允许接受其他参数
    return function (context) {
      const dispatch = index => {
        // 将中间件函数调用包装为一个 Promise 兼容异步处理
        return Promise.resolve(
          // fn(ctx, next)
          fn(context, () => dispatch(index + 1))
      // 返回并调用第一个中间件处理函数
      return dispatch(0)
  // 构造上下文对象
  createContext(req, res) {
    // 为了避免请求之间 context 数据交叉污染
    // 这里为每个请求单独创建 context 对象
    const context = Object.create(this.context)
    // 在 context 中可以获取 Request
    const request = context.request = Object.create(this.request)
    // 在 context 中可以获取 Response
    const response = context.response = Object.create(this.response)
    context.app = request.app = response.app = this
    // 原生 Node 请求/响应对象
    context.req = request.req = response.req = req
    context.res = request.res = response.res = res
    // 在 Request 和 Respon 中也可以获取 context 上下文对象
    request.ctx = response.ctx = context
    // Requset 中也可以获取 Response
    request.response = response
    // Response 中也可以获取 Requset
    response.request = request
    // 没有经过任何处理的请求路径
    context.originUrl = request.originUrl = req.url
    // 初始化 state 数据对象,用于给模板视图提供数据
    context.state = {}
    return context
  callback() {
    // 获取调用第一个中间件函数的方法
    const fnMiddleware = this.compose(this.middleware)
    const handleRequest = (req, res) => {
      // 每个请求都会创建一个独立的 Context 对象,它们之间不会互相污染
      const context = this.createContext()
      // 开始执行第一个中间件函数
      fnMiddleware(context).then(() => {
        console.log('end')
        res.end('My Koa')
      }).catch(err => {
        console.log(err.message)
    return handleRequest
module.exports = Application
// my-koa\lib\context.js
const context = {}
module.exports = context
// my-koa\lib\request.jsmy-koa\lib\request.js/
const request = {}
module.exports = request
// my-koa\lib\request.jsmy-koa\lib\response.js/
const response = {}
module.exports = response

扩展 Request 和 Response

使用对象的访问器属性(get 和 set)动态获取和设置属性。

// my-koa\lib\request.jsmy-koa\lib\request.js/
const url = require('url')
const request = {
  get header() {
    return this.req.headers
  set header(val) {
    this.req.headers = val
  get headers() {
    return this.req.headers
  set headers(val) {
    this.req.headers = val
  get url() {
    return this.req.url
  get path() {
    return url.parse(this.req.url).pathname
  get query() {
    return url.parse(this.req.url, true).query
  get method() {
    return this.req.method
module.exports = request
// my-koa\lib\request.jsmy-koa\lib\response.js/
const response = {
  set status(val) {
    this.res.statusCode = val
  set message(msg) {
    this.res.statusMessage = msg;
module.exports = response

处理 Context 中的代理别名

// my-koa\lib\context.js
const context = {
  get method() {
    return this.request.method
  get header() {
    return this.request.header
module.exports = context

可以看到 context 别名的 getter 函数处理逻辑都一样(归功于 request 中定义了同名的属性),所以可以将设置别名的操作提取为一个方法:

// my-koa\lib\context.js
const context = {}
definePorpperty('request', 'method')
definePorpperty('request', 'header')
definePorpperty('request', 'url')
definePorpperty('request', 'path')
definePorpperty('request', 'query')
function definePorpperty(target, name) {
  Object.defineProperty(context, name, {
    get() {
      return this[target][name]
    set(value) {
      this[target][name] = value
module.exports = context
 

注意:Koa 中使用的 delegates 包,内部使用 Object.prototype.__defineGetter__()MDN)和 Object.prototype.__defineSetter__()MDN)设置 get/set 属性,不过该特性已从 Web 标准中删除。

设置和发送 body 数据

  • 本质上使用的是 node 的 Response 对象发送数据
  • 多次设置 body 最终响应的应该是最后一次设置的内容
app.use((ctx, next) => {
  ctx.body = 'Hello Koa1'
  next()
  ctx.body = 'Hello Koa3'
app.use((ctx, next) => {
  ctx.body = 'Hello Koa2'
// 最终应该响应 Hello Koa3

在 Response 对象中设置 body 的 getter 和 setter:

// my-koa\lib\request.jsmy-koa\lib\response.js/
const response = {
  set status(val) {
    this.res.statusCode = val
  set message(msg) {
    this.res.statusMessage = msg;
  _body: '', // 真正用来存数据的属性
  get body() {
    return this._body
  set body(val) {
    this._body = val
module.exports = response

添加 context 的 body 别名:

// my-koa\lib\context.js
definePorpperty('response', 'body')

执行完中间件后(洋葱圈从进到出)将 body 返回给客户端:

// my-koa\lib\application.js
class Application {
  callback() {
    const fnMiddleware = this.compose(this.middleware)
    const handleRequest = (req, res) => {
      const context = this.createContext(req, res)
      // 开始执行第一个中间件函数,并传入上下文对象
      fnMiddleware(context).then(() => {
        res.end(context.body)
      }).catch(err => {
        res.end(err.message)
    return handleRequest
module.exports = Application

处理 body 数据格式

response.body 支持一下格式,如果 res.status 没有赋值,Koa会自动设置为 200204。:

// app.js
// const Koa = require('koa')
const Koa = require('./my-koa')
const fs = require('fs')
const fsPromises = require('fs').promises
const app = new Koa()
app.use(async (ctx, next) => {
  // 字符串
  ctx.body = 'string'
  // // 数字
  // ctx.body = 123
  // // buffer
  // ctx.body = await fsPromises.readFile('./package.json')
  // // 文件流
  // ctx.body = fs.createReadStream('./package.json')
  // // 对象&数组会转化成 JSON 字符串
  // ctx.body = { foo: 'bar' }
  // ctx.body = [1, 2, 3]
  // // 无响应内容
  // ctx.body = null
app.listen(3000, () => {
  console.log('server is running on http://localhost:3000')

执行完中间件后,调用一个方法处理 body 并返回给客户端:

// my-koa\lib\application.js
// 引入 node 原生 stream 构造函数
const Stream = require('stream')
class Application {
  callback() {
    const fnMiddleware = this.compose(this.middleware)
    const handleRequest = (req, res) => {
      const context = this.createContext(req, res)
      fnMiddleware(context).then(() => {
        // res.end(context.body)
        // 调用函数处理 body
        respond(context)
      }).catch(err => {
        res.end(err.message)
    return handleRequest
function respond(ctx) {
  const body = ctx.body
  const res = ctx.res
  // 字符串 和 Buffer 直接返回
  if (typeof body === 'string') return res.end(body)
  if (Buffer.isBuffer(body)) return res.end(body)
  // 可读流通过管道发送给可写流(res)
  if (body instanceof Stream) return body.pipe(res)
  // 数字转化成字符串
  if (typeof body === 'number') return res.end(body + '')
  // 对象和数组转化成 JSON 字符串
  if (body !== null && typeof body === 'object') {
    const jsonStr = JSON.stringify(body)
    return res.end(jsonStr)
  res.statusCode = 204
  res.end()
module.exports = Application
                                    koa是一个基于node实现的一个新的web框架,它是由express框架的原班人马打造的。它的特点是优雅、洁、表达力强、自由度高。它更express相比,它是一个更轻量的node框架,因为它所有功能都通过插件实现,这种插拔式的架构设计模式,很符合unix哲学。koa框架现在更新到了2.x版本,本文从零开始,循序渐进,讲解koa2的框架源码结构和实现原理,展示和详解koa2框架源码中的几个最重要的概念,然后手把手教大家亲自实现一个易的koa2框架,帮助大家学习和更深层次的理解koa2,看完本文以后,再去对照koa2的源码进行查看,相信你的思路将会非常的顺畅。本文所用的框架是koa2,它跟k
                                    最近尝试用了一下Koa,并在此记录一下使用心得。注意:本文是以读者已经了解Generator和Promise为前提在写的,因为单单Generator和Promise都能够写一篇博文来讲解介绍了,所以就不在这里赘述。网上资料很多,可以自行查阅。Koa是Express原班人马打造的一个更小,基于nodejs平台的下一代web开发框架。Koa的精妙之处就在于其使用generator和promise,实现了一种更为有趣的中间件系统,Koa的中间件是一系列generator函数的对象,执行起来有点类似于栈的结构,依次执行。同时也类似于Python的django框架的中间件系统,以前苏千大神做分享的时候把
                                    大家好我是你们的小圆。
经过了之前的学习,我们了解了pipline的基本使用方式,那么我们来一个小的实战,如果之前的文章你还没有看到,可以点击上方的话题,我都收录在了话题中。或者点击下面的链接。
流水线pipeline招式之声明式(前篇)
流水线pipeline招式之声明式(中篇)
流水线pipeline招式之声明式(后篇)
之前jenkins构建的时候,我们完成了一个自由风格的构建,文章我放在这里:
RUN,第一个Jenkins构建
那么,我们可不可以用pipline的方式去实现它,...
                                    这个流程是一个典型的web服务,与其我们使用java或者nodejs来搭建一个web服务器,不如我们就使用python自己的web框架来实现这一目标,减少技术栈的同时,还可以实现代码逻辑的统一,何乐而不为呢?venv跟conda的env很类似,都是用来创建虚拟环境,从而实现不同的环境进行分离的作用。这里的方法体中我们调用了python的f函数来对字符串进行格式化,在内部为了防止web输入端的恶意注入,这里引用了markupsafe的escape方法,可以对输入的字符串进行转义,从而避免了恶意的攻击。
	npm install koa-router @types/koa-router -s
koa中间件:用于获取提交数据
	npm install koa-bodyparser 
const http = require('http');
const request = require('./lib/request');
const response = require('./lib/respons
                                    1. Koa是什么?一个以nodejs为基础的一个后台框架。直白一点来说,就是一个javascript语言需要编写的库,它的定位是作为服务端应用提供服务,本身对外暴露了一...
                                    request pipe 是异步下载图片的,如果需要同步需要添加一个Promisevar myP = new Promise(function (resolve, reject) {
                var stream = request(mainItem.pic).pipe(
                    fs.createWriteStream(filepath)
                                    介flask是一个轻量级的基于Python的web框架。它没有太多复杂的功能,就像koa,需要一系列的插件来扩展其他功能,被称为microframework。flask没有默认使用的数据库等。其他Python web框架:Django:它比flask重的多。包含了web开发的常用功能,orm、session、form、admin、分页、中间件、信号、缓存、contenttype等等,Django...
                                    1、下载koa2npm install --save koa@2.0.02、引入koa类const Koa = require('koa');创建Koa的实例const app = new Koa();3、使用创建的实例处理异步请求(use方法)app.use(async (ctx, next) => {
    ctx.response.type = 'text/html';
    ctx.re
                                    在js代码中,如果我们读取数据或者是打印数据,一般是var 或 let 一个变量,再打印出来但是这样的方法需要开辟一个新的内存,来保存这个变量当我们在网页上读取内存较大的文件时(比如视频、图片等),写入写出会大的占用内存这时候就需要在node.js中提供给我们的流——streamstream可以边读边写,这样就可以更好的不占用太多内存,并且完成所需任务stream分为四种:readable、wr...