koa2 源码解读 application
koa2的源码比较简单,重点解读aplication,
其中context源码比较简单,主要是一些error cookies等,重点可以关注下delegate,delegate模块中,主要通过prototype的方式进行属性的增加。
request和response两个模块都是get set的一些基础api及封装的node原始方法
application
'use strict'; //严格模式 /**
* Module dependencies.
*/ const isGeneratorFunction = require('is-generator-function');
const debug = require('debug')('koa:application');
const onFinished = require('on-finished');
const response = require('./response');
const compose = require('koa-compose');
const isJSON = require('koa-is-json');
const context = require('./context');
const request = require('./request');
const statuses = require('statuses');
const Emitter = require('events');
const util = require('util');
const Stream = require('stream');
const http = require('http');
const only = require('only');
const convert = require('koa-convert');
const deprecate = require('depd')('koa');
const { HttpError } = require('http-errors'); /**
* constructor() 构造函数
* listen() 调用原生http模块创建服务并监听
* use() 中间件处理
* callback() http请求的回调函数
* handleRequest() 请求真正的回调函数
* createContext() 创建上下文对象
* respond() 所有中间件处理完后自动响应
* onerror() 处理错误信息
*
*/ /**
* Expose `Application` class.
* Inherits from `Emitter.prototype`.
*/ module.exports = class Application extends Emitter {
/**
* Initialize a new `Application`.
*
* @api public
*/ /**
*
* @param {object} [options] Application options
* @param {string} [options.env='development'] Environment
* @param {string[]} [options.keys] Signed cookie keys
* @param {boolean} [options.proxy] Trust proxy headers
* @param {number} [options.subdomainOffset] Subdomain offset
*
*/ constructor(options) {
super();
options = options || {};
this.proxy = options.proxy || false; //是否允许跨域
this.subdomainOffset = options.subdomainOffset || 2; // 子域名允许请求几级连接
this.env = options.env || process.env.NODE_ENV || 'development'; //node的执行环境
if (options.keys) this.keys = options.keys;
this.middleware = []; //所有的中间件的存入
this.context = Object.create(context); //每次实例化都重新赋值,为保证多次实例化时保持不冲突,和单例模式成反例
this.request = Object.create(request);
this.response = Object.create(response);
if (util.inspect.custom) {
this[util.inspect.custom] = this.inspect; //保存88行代码中的内容
}
} /**
* Shorthand for:
*
* http.createServer(app.callback()).listen(...)
*
* @param {Mixed} ...
* @return {Server}
* @api public
*/ listen(...args) {
debug('listen');
const server = http.createServer(this.callback()); //原生http模块创建服务并监听
return server.listen(...args);
} /**
* Return JSON representation.
* We only bother showing settings.
*
* @return {Object}
* @api public
*/ toJSON() {
return only(this, [ //only 对传入的数据使用reduce进行重组
'subdomainOffset',
'proxy',
'env'
]);
} /**
* Inspect implementation.
*
* @return {Object}
* @api public
*/ inspect() {
return this.toJSON(); //数据重组
} /**
* Use the given middleware `fn`.
*
* Old-style middleware will be converted.
*
* @param {Function} fn
* @return {Application} self
* @api public
*/ use(fn) {
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!');//必须是一个function
if (isGeneratorFunction(fn)) {
deprecate('Support for generators will be removed in v3. ' +
'See the documentation for examples of how to convert old middleware ' +
'https://github.com/koajs/koa/blob/master/docs/migration.md');
fn = convert(fn);
}
debug('use %s', fn._name || fn.name || '-'); //DEBUG=koa* node --harmony app.js 调试时输出中间件调用及时长
this.middleware.push(fn); //将中间件加入到middleware数组中
return this;
} /**
* Return a request handler callback
* for node's native http server.
*
* @return {Function}
* @api public
*/ callback() {
const fn = compose(this.middleware); //将这些中间件组合后拿到执行链函数fn if (!this.listenerCount('error')) this.on('error', this.onerror); //如果没有监听则报错 const handleRequest = (req, res) => { //事件处理函数
const ctx = this.createContext(req, res); //创建一个ctx
return this.handleRequest(ctx, fn); //交给157行的handleRequest
}; return handleRequest;
} /**
* Handle request in callback.
*
* @api private
*/ handleRequest(ctx, fnMiddleware) {
const res = ctx.res;
res.statusCode = 404; //初始赋值
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx); //211行详解
onFinished(res, onerror);
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
} /**
* Initialize a new context.
*
* @api private
*/ createContext(req, res) {
const context = Object.create(this.context);//通过context对象的原型创建
const request = context.request = Object.create(this.request);//通过request对象的原型创建,this.request指的是原生的request,修改this.request中的属性就是修改原生的对应的属性数据
const response = context.response = Object.create(this.response);//通过response对象的原型创建
context.app = request.app = response.app = this; //传递
context.req = request.req = response.req = req;
context.res = request.res = response.res = res;
request.ctx = response.ctx = context;
request.response = response; //交叉传递
response.request = request;
context.originalUrl = request.originalUrl = req.url;
context.state = {};
return context;
} /**
* Default error handler.
*
* @param {Error} err
* @api private
*/ onerror(err) {
if (!(err instanceof Error)) throw new TypeError(util.format('non-error thrown: %j', err)); //检测err不是Error实例时,创建一个Error的实例 if (404 == err.status || err.expose) return;
if (this.silent) return; const msg = err.stack || err.toString(); //将err堆栈的信息拿出来
console.error(); //控制台打印Error信息
console.error(msg.replace(/^/gm, ' '));
console.error();
}
}; /**
* Response helper.
*/ function respond(ctx) {
// allow bypassing koa
if (false === ctx.respond) return; //允许绕过KOA 为写入原始的res对象而不是让koa处理你的rsponse if (!ctx.writable) return; const res = ctx.res;
let body = ctx.body; //外面Middleware传入的body数据
const code = ctx.status; //当前状态码 // ignore body
if (statuses.empty[code]) { //当前状态码是空的,则清掉body并结束
// strip headers
ctx.body = null;
return res.end();
} if ('HEAD' == ctx.method) { //head部分
if (!res.headersSent && isJSON(body)) { //判断当前header没有被发送并且是 重组后的json数据
ctx.length = Buffer.byteLength(JSON.stringify(body)); //则重新序列化 取长度
}
return res.end();
} // status body
if (null == body) { // body部分 不为null
if (ctx.req.httpVersionMajor >= 2) { //根据http major的版本 分别对body进行初始化
body = String(code);
} else {
body = ctx.message || String(code);
}
if (!res.headersSent) {
ctx.type = 'text';
ctx.length = Buffer.byteLength(body);
}
return res.end(body);
}
//以下为支持各种格式的body
// responses
if (Buffer.isBuffer(body)) return res.end(body);
if ('string' == typeof body) return res.end(body);
if (body instanceof Stream) return body.pipe(res); // body: json
body = JSON.stringify(body);
if (!res.headersSent) {
ctx.length = Buffer.byteLength(body);
}
res.end(body);
} /**
* Make HttpError available to consumers of the library so that consumers don't
* have a direct dependency upon `http-errors`
*/
module.exports.HttpError = HttpError;
koa2 源码解读 application的更多相关文章
- koa2源码解读及实现一个简单的koa2框架
阅读目录 一:封装node http server. 创建koa类构造函数. 二:构造request.response.及 context 对象. 三:中间件机制的实现. 四:错误捕获和错误处理. k ...
- koa2源码解读
最近在复习node的基础知识,于是看了看koa2的源码,写此文分享一下包括了Koa2的使用.中间件及上下文对象的大致实现原理. koa的github地址:https://github.com/koaj ...
- koa源码解读
koa是有express原班人马打造的基于node.js的下一代web开发框架.koa 1.0使用generator实现异步,相比于回调简单和优雅和不少.koa团队并没有止步于koa 1.0, 随着n ...
- SDWebImage源码解读之SDWebImageCache(下)
第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...
- AFNetworking 3.0 源码解读 总结(干货)(下)
承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...
- AFNetworking 3.0 源码解读 总结(干货)(上)
养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...
- AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager
让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心. 前言 AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控 ...
- AFNetworking 3.0 源码解读(八)之 AFImageDownloader
AFImageDownloader 这个类对写DownloadManager有很大的借鉴意义.在平时的开发中,当我们使用UIImageView加载一个网络上的图片时,其原理就是把图片下载下来,然后再赋 ...
- AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization
这篇就讲到了跟请求相关的类了 关于AFNetworking 3.0 源码解读 的文章篇幅都会很长,因为不仅仅要把代码进行详细的的解释,还会大概讲解和代码相关的知识点. 上半篇: URI编码的知识 关于 ...
随机推荐
- VirtualStringTree常用类和属性
重要的类:TBaseVirtualTree = class(TCustomControl)TCustomVirtualStringTree = class(TBaseVirtualTree)TVirt ...
- selenium自动新增店铺
说明:仅作为参考练习,代码中涉及数据均为测试数据. from selenium import webdriver from selenium.webdriver import ActionChains ...
- 搜索---BFS
搜索 深度优先搜索和广度优先搜索广泛的应用于树和图中,但是他们的应用远不止于此. BFS 广度优先搜索一层一层地进行遍历,每层遍历都是以上一层遍历的结果作为起点,遍历一个距离能访问到的所有节点 ...
- wxpython菜单栏、子菜单栏、弹出菜单栏、状态栏小程序学习源代码分享
#coding=utf-8 import wx class MyFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self,None,-1, ...
- 多个nginx之间如何实现反向代理和负责均衡
1)nginx反向代理: http { upstream routeadmin { ip_hash; server 127.0.0.1:9201 weight= ...
- C# 委托和事件 实现窗体间的通信
例子 : 点击form1上的按钮打开form2窗口,在form2窗体中的文本框中输入一个值后,在点击form2窗体中按钮,在form2中的文本框中输入的值也会在form1中的文本框中出现. form1 ...
- Asp.Net MVC 5使用Identity之简单的注册和登陆
由于.Net MVC 5登陆和注册方式有很多种,但是Identity方式去实现或许会更简单更容易理解 首先新建一个项目 其次如下选择Empty和MVC的选项 然后打开NuGet包管理器分别安装几个包 ...
- 实现单选框点击label标记中的文字也能选中
实例: <label for="man"> <input type="radio" value="男" name=&quo ...
- 点击按钮时,显示不同的div内容
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- glibc2.22