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编码的知识 关于 ...
随机推荐
- Jenkins安装配置 远程发布SpringBoot项目
环境要求: Java : 1.8.0_161. Maven :http://maven.apache.org/download.cgi 3.6.1 下载完解压,配置环境变量:vim /etc/prof ...
- C++ 函数返回对象时并没有调用拷贝构造函数
#include <iostream> #include <vector> #include <string.h> using namespace std; cla ...
- HTMLTestRunner_PY3脚本代码
HTMLTestRunner_PY3.py文件代码如下: # -*- coding: utf-8 -*- """ A TestRunner for use with th ...
- 2019牛客暑期多校训练营(第三场) - J - LRU management - 模拟
https://ac.nowcoder.com/acm/contest/883/J 根据这个数据结构的特点,也就是计算机组成原理里面学过的cache的LRU管理算法,每次访问都会在cache中查询一页 ...
- 14、前端知识点--Vue生命周期浅析
vue生命周期 每个Vue实例或组件从创建到显示再到废弃的过程就是vue的生命周期.很多时候我们希望能在这个过程中执行一些操作,于是就有了生命周期钩子. 生命周期钩子函数允许我们在实例不同阶段执行各种 ...
- 定制团队自己的 Vue template
一,我们使用vue-cli 可以快速初始化vue.js的项目,官方提供了webpack,pwa,browserify-sipmple,等常用template 二.置知识1,模板结构template:该 ...
- IntelliJ IDEA 创建spring boot 的Hello World 项目
1.Open IDEA,choose "New-->Project" 2.Choose "Spring Initializr" 3. Choose jav ...
- restTemplate工具类
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.sprin ...
- Windows 好用的护眼软件
目录 1. 按 2. Windows10自带夜间模式 3. Iris Pro 3.1. 介绍 3.1.1. 保护用眼,改善睡眠 3.1.2. ×9 种不同的预设搭配 3.1.3. 计时器 3.1.4. ...
- Linux--shell三剑客<sed>--07
1.sed(stream editor): 作为行编辑器,对文本进行编辑(以行为单位) 默认显示输出所有文件内容 注意:sed编辑文件,却不改变原文件 2.sed的工作原理: 指定一个文本文件,依次读 ...