本系列文章是本人学习相关知识时所积累的笔记,以记录自己的学习历程,也为了方便回顾知识;故文章内容较为随意简练,抱着学习目的来的同学务必转移他处,以免我误人子弟~
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中间件的原理
- Koa将中间件按顺序存放在数组中
- 每当中间件执行到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){
// ....
}