Koa源码

本系列文章是本人学习相关知识时所积累的笔记,以记录自己的学习历程,也为了方便回顾知识;故文章内容较为随意简练,抱着学习目的来的同学务必转移他处,以免我误人子弟~

koa的使用

创建后端服务器

原生的http模块提供的方法:

const http = require('http');

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

server.listen(3000, () => {
  console.log('server start at 3000');
});

在回调函数中,使用req和res对请求数据和响应数据进行处理
Koa创建服务:

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

app.use(async (ctx, next) => {
  let start = new Date().getTime();
  await next();
  let time = new Date().getTime() - start;
  ctx.set("X-Response-Time", `${time}ms`);
});

app.use(async (ctx, next) => {
  ctx.status = 200;
  ctx.body = 'Hello World';
  await next();
});

app.listen(3000);

Koa使用use方法接收中间件
ctx是对原生req和res的封装,而next是中间件的关键,调用next实际上是跳出当前中间件进入下一个中间件

所谓中间件即是同步或异步函数

koa中间件的原理

  1. Koa将中间件按顺序存放在数组中
  2. 每当中间件执行到next时,会进入下一个中间件,即内部实现将下一个中间件传递给next

本系列文章是本人学习相关知识时所积累的笔记,以记录自己的学习历程,也为了方便回顾知识;故文章内容较为随意简练,抱着学习目的来的同学务必转移他处,以免我误人子弟~

cookies用途

Koa中使用cookies模块,封装获取前端请求的cookie和后端要求前端存储cookie的功能;

app.use(async (ctx) => {
  // 设置cookie,前端接收到响应后将cookie存放本地
  ctx.cookies.set(
    'cookieName',
    'cookieValue',
    {
      domain: 'localhost',  // 写cookie所在的域名
      path: '/index',       // 写cookie所在的路径
      maxAge: 10 * 60 * 1000, // cookie有效时长
      expires: new Date('2020-02-15'),  // cookie失效时间
      httpOnly: false,  // 是否只用于http请求中获取
      overwrite: false  // 是否允许重写
    }
  )
  // 获取前端请求中名称为“otherCookieName”的cookie值
  ctx.cookies.get("otherCookieName");
})

cookies函数原理

keygrip模块

js操作符:^ 异或
规则:位不相同时才为1;


//http://codahale.com/a-lesson-in-timing-attacks/
var constantTimeCompare = function(val1, val2){
    if(val1 == null && val2 != null){
        return false;
    } else if(val2 == null && val1 != null){
        return false;
    } else if(val1 == null && val2 == null){
        return true;
    }

    if(val1.length !== val2.length){
        return false;
    }

    var result = 0;

    for(var i = 0; i < val1.length; i++){
        result |= val1.charCodeAt(i) ^ val2.charCodeAt(i); //Don't short circuit
    }

    return result === 0;
};

本系列文章是本人学习相关知识时所积累的笔记,以记录自己的学习历程,也为了方便回顾知识;故文章内容较为随意简练,抱着学习目的来的同学务必转移他处,以免我误人子弟~

delegate函数原理

Koa源码中,是这么使用delegate


/**
 * Response delegation.
 */

delegate(proto, 'response')
  .method('attachment')
  .method('redirect')
  .method('remove')
  .method('vary')
  .method('set')
  .method('append')
  .method('flushHeaders')
  .access('status')
  .access('message')
  .access('body')
  .access('length')
  .access('type')
  .access('lastModified')
  .access('etag')
  .getter('headerSent')
  .getter('writable');

/**
 * Request delegation.
 */

delegate(proto, 'request')
  .method('acceptsLanguages')
  .method('acceptsEncodings')
  .method('acceptsCharsets')
  .method('accepts')
  .method('get')
  .method('is')
  .access('querystring')
  .access('idempotent')
  .access('socket')
  .access('search')
  .access('method')
  .access('query')
  .access('path')
  .access('url')
  .access('accept')
  .getter('origin')
  .getter('href')
  .getter('subdomains')
  .getter('protocol')
  .getter('host')
  .getter('hostname')
  .getter('URL')
  .getter('header')
  .getter('headers')
  .getter('secure')
  .getter('stale')
  .getter('fresh')
  .getter('ips')
  .getter('ip');

实现的功能是将ctx.response和ctx.request的属性代理到ctx上,即可以通过ctx.path访问到ctx.request.path,可以通过ctx.body = someObj对ctx.response.body赋值someObj

看delegate源码:

// delegate源码
function Delegator(proto, target) {
  if (!(this instanceof Delegator)) return new Delegator(proto, target);
  this.proto = proto;
  this.target = target;
  this.methods = [];
  this.getters = [];
  this.setters = [];
  this.fluents = [];
}

第一句的作用是这个函数可以当成构造函数来使用,也可以想普通函数一样掉用,如果像普通函数调用,则触发第一句代码,最终仍然返回实例,所以Koa源码的用法也可改为:

let delegate = new delegate(proto,"response");
delegate
	.access("body")
	...

method方法:代理方法

// delegate源码
Delegator.prototype.method = function(name){
  var proto = this.proto;
  var target = this.target;
  this.methods.push(name);

  proto[name] = function(){
    return this[target][name].apply(this[target], arguments);
  };

  return this;
};

getter方法:代理属性get行为

// delegate源码
Delegator.prototype.getter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.getters.push(name);

  proto.__defineGetter__(name, function(){
    return this[target][name];
  });

  return this;
};

setter方法:代理属性set行为

// delegate源码
Delegator.prototype.setter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.setters.push(name);

  proto.__defineSetter__(name, function(val){
    return this[target][name] = val;
  });

  return this;
};

access方法:代理属性get和set行为

// delegate源码
Delegator.prototype.access = function(name){
  return this.getter(name).setter(name);
};

.__defineSetter__.__defineGetter__现在已经快被废弃,所以我们对于getter、setter和access方法可以进行如下改写:

// getter
Delegator.prototype.getter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.getters.push(name);

  Object.defineProperty(proto,name,{
    get(){
      return proto[target][name];
    }
  })
  return this;
}
// setter
Delegator.prototype.setter = function(name){
  var proto = this.proto;
  var target = this.target;
  this.setters.push(name);

  Object.defineProperty(proto,name,{
    set(value){
		proto[target][name] = value;
    }
  })
  return this;
}
// accsee
Delegator.prototype.access = function(name){
  Object.defineProperty(proto,name,{
    get(){
      return proto[target][name];
    },
    set(value){
      proto[target][name] = value;
    }})
  return this;
}

delegate源码中还有个fluent方法,koa中没有用到

// delegate源码
Delegator.prototype.fluent = function (name) {
  var proto = this.proto;
  var target = this.target;
  this.fluents.push(name);

  proto[name] = function(val){
    if ('undefined' != typeof val) {
      this[target][name] = val;
      return this;
    } else {
      return this[target][name];
    }
  };

  return this;
};

作用跟access相近,但是改变了取值和赋值方式:

// access
ctx.body // 返回 ctx.response.body 的值
ctx.body = someObj // ctx.response.body 的值变为 someObj

// fluent
ctx.body() // 返回 ctx.response.body 的值
ctx.body(someObj) // ctx.response.body 的值变为 someObj

本系列文章是本人学习相关知识时所积累的笔记,以记录自己的学习历程,也为了方便回顾知识;故文章内容较为随意简练,抱着学习目的来的同学务必转移他处,以免我误人子弟~

compose函数原理

compose实现:


function compose (middleware) {
  if (!Array.isArray(middleware)) throw new TypeError('Middleware stack must be an array!')
  for (const fn of middleware) {
    if (typeof fn !== 'function') throw new TypeError('Middleware must be composed of functions!')
  }

  return function (context, next) {
    let index = -1
    return dispatch(0)
    function dispatch (i) {
      if (i <= index) return Promise.reject(new Error('next() called multiple times'))
      index = i
      let fn = middleware[i]
      if (i === middleware.length) fn = next // 这里的next永远为undefined,因为调用该函数时没有传next参数
      if (!fn) return Promise.resolve()
      try {
        return Promise.resolve(fn(context, dispatch.bind(null, i + 1)));
      } catch (err) {
        return Promise.reject(err)
      }
    }
  }
}

compose函数接收中间件数组,返回一个函数,这个函数利用内部定义的dipatch函数递归遍历中间件(这个函数最终在 Koa.prototype.handleRequest中调用)

个人感觉Koa在这边处理方式有点绕
更直观的可以这么写:

handleRequest(req, res) {
    let ctx = this.createContext(req, res);
    let fn = this.compose(this.middlewares, ctx);
    fn.then((value)=>handleResponse(ctx)).catch((err)=>{...});
}
createContext(req, res) {
    let ctx = Object.create(this.context);
    const request = ctx.request = Object.create(this.request);
    const response = ctx.response = Object.create(this.response);
    ctx.req = request.req = response.req = req;
    ctx.res = request.res = response.res = res;
    request.ctx = response.ctx = ctx;
    request.response = response;
    response.request = request;
    return ctx;
}
compose(middlewares, ctx) {
    function dipatch(index) {
        if (index === middlewares.length) {
            return Promise.resolve();
        }
        let middleware = middlewares[index];
        return Promise.resolve(middleware(ctx, () => dipatch(index + 1)));
    }
    return dipatch(0);
}
handleResponse(ctx){
    // ....
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值