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的更多相关文章

  1. koa2源码解读及实现一个简单的koa2框架

    阅读目录 一:封装node http server. 创建koa类构造函数. 二:构造request.response.及 context 对象. 三:中间件机制的实现. 四:错误捕获和错误处理. k ...

  2. koa2源码解读

    最近在复习node的基础知识,于是看了看koa2的源码,写此文分享一下包括了Koa2的使用.中间件及上下文对象的大致实现原理. koa的github地址:https://github.com/koaj ...

  3. koa源码解读

    koa是有express原班人马打造的基于node.js的下一代web开发框架.koa 1.0使用generator实现异步,相比于回调简单和优雅和不少.koa团队并没有止步于koa 1.0, 随着n ...

  4. SDWebImage源码解读之SDWebImageCache(下)

    第六篇 前言 我们在SDWebImageCache(上)中了解了这个缓存类大概的功能是什么?那么接下来就要看看这些功能是如何实现的? 再次强调,不管是图片的缓存还是其他各种不同形式的缓存,在原理上都极 ...

  5. AFNetworking 3.0 源码解读 总结(干货)(下)

    承接上一篇AFNetworking 3.0 源码解读 总结(干货)(上) 21.网络服务类型NSURLRequestNetworkServiceType 示例代码: typedef NS_ENUM(N ...

  6. AFNetworking 3.0 源码解读 总结(干货)(上)

    养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...

  7. AFNetworking 3.0 源码解读(九)之 AFNetworkActivityIndicatorManager

    让我们的APP像艺术品一样优雅,开发工程师更像是一名匠人,不仅需要精湛的技艺,而且要有一颗匠心. 前言 AFNetworkActivityIndicatorManager 是对状态栏中网络激活那个小控 ...

  8. AFNetworking 3.0 源码解读(八)之 AFImageDownloader

    AFImageDownloader 这个类对写DownloadManager有很大的借鉴意义.在平时的开发中,当我们使用UIImageView加载一个网络上的图片时,其原理就是把图片下载下来,然后再赋 ...

  9. AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization

    这篇就讲到了跟请求相关的类了 关于AFNetworking 3.0 源码解读 的文章篇幅都会很长,因为不仅仅要把代码进行详细的的解释,还会大概讲解和代码相关的知识点. 上半篇: URI编码的知识 关于 ...

随机推荐

  1. CentOS7 通过YUM安装MySQL5.7 linux

    CentOS7 通过YUM安装MySQL5.7 1.进入到要存放安装包的位置 cd /home/lnmp 2.查看系统中是否已安装 MySQL 服务,以下提供两种方式: rpm -qa | grep  ...

  2. RocketMQ安装部署及整合Springboot

    消息中间件的功能: 通过学习ActiveMq,kafka,rabbitMq这些消息中间件,我们大致能为消息中间件的功能做一下以下定义:可以先从基本的需求开始思考 最基本的是要能支持消息的发送和接收,需 ...

  3. Maven-maven插件(1)添加主类信息到MANIFEST.MF

    1.以前面的HelloWorld项目为例,在pom.xml中添加如下代码,指定插件 <build> <plugins> <plugin> <groupId&g ...

  4. 在学习linux磁盘管理期间学习的逻辑卷管理笔记

    LVM(逻辑分区)的创建顺序:物理分区-物理卷-卷组-逻辑卷-挂载. 物理卷(Physical Volume,PV):就是指硬盘分区,也可以是整个硬盘或已创建的软RAID,是LVM的基本存储设备. 卷 ...

  5. python里的排序

    本篇文章主要讲: 自定义规则排序 多字段排序 开讲之前,先讲一些简单sorted()或者sort(),两者返回值不同!大家自行学习,不是本文的重点! sorted([5, 2, 3, 1, 4]) # ...

  6. django 开发中数据库做过什么优化??

    1.设计表时,尽量少使用外键,因为外键约束会影响插入和删除性能: 2.使用缓存,减少对数据库的访问: 3.在 orm 框架下设置表时,能用 varchar 确定字段长度时,就别用 text: 4.可以 ...

  7. 洛谷 - P3391 【模板】文艺平衡树(Splay) - 无旋Treap

    https://www.luogu.org/problem/P3391 使用无旋Treap维护序列,注意的是按顺序插入的序列,所以Insert实际上简化成直接root和Merge合并,但是假如要在序列 ...

  8. HTML5中的Web Worker

    什么是 Web Worker? 当在 HTML 页面中执行脚本时,页面是不可响应的,直到脚本已完成. Web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能.您 ...

  9. Django 路由层与视图层

    1.路由层 1.1无名分组 1.2 有名分组 1.3 反向解析 1.4 路由分发 1.5 名称空间 2.伪静态网页 3.虚拟环境 4.视图层 1.1 JsonResponse 1.2 FBV与CBV ...

  10. Rsync+sersync部署

    内核版本:2.6.32-431.el6.x86_64 系统采用最小化安装,系统经过了基本优化,selinux 为关闭状态,iptables 为无限制模式 源码包存放位置:/root Rsync 客户端 ...