本系列文章是本人學習相關知識時所積累的筆記,以記錄自己的學習歷程,也爲了方便回顧知識;故文章內容較爲隨意簡練,抱着學習目的來的同學務必轉移他處,以免我誤人子弟~
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){
// ....
}