предисловие
В этой статье с самого начала анализируется основной исходный код очень популярного фреймворка koa, подходящий для разработчиков, освоивших использование фреймворка koa.
основной механизм
Теперь давайте начнем с нуля и посмотрим, что именно происходит внутри коа?
// usage
const app = new Koa()
// source code
constructor() {
super();
this.proxy = false;
this.middleware = [];
this.subdomainOffset = 2;
this.env = process.env.NODE_ENV || 'development';
this.context = Object.create(context);
this.request = Object.create(request);
this.response = Object.create(response);
}
Это источник всего,Koa
Экземпляры класса рождаются из этого, наследуя отEvents
, вы можете знать, что его подклассы могут обрабатывать асинхронные события, ноKoa
Как с этим бороться пока неизвестно, поставим сначала знак вопроса. Однако в процессе создания экземпляра может быть известно, что в качестве атрибутов экземпляра инициализируются три объекта, а именноcontext
request
response
, и знакомый массив всего глобального ПО промежуточного слояmiddleware
значок класса коа
// usage
app.use(async (ctx, next) => {
// 中间件函数
});
При вызове метода использования, после подтверждения того, что онasync
В случае функции, пройдитеpush
операция, эта функция будет добавлена кmiddleware
в массиве
use(fn) {
// 类型检查...
this.middleware.push(fn);
return this;
}
На данный момент у нас уже есть операции для обработки, ноkoa
на самом деле не бежал
app.listen(3000);
так сказать при включенииhttp
Когда серверkoa
чтобы действительно начать иметь дело с нашимиhttp
запрос, так что же именно скрывается за таким аккуратным вызовом?
const server = http.createServer(this.callback());
return server.listen(...args);
koa
использовалnode
роднойhttp
пакет для созданияhttp服务
, все секреты скрыты вcallback()
в этом методе
// source code
const fn = compose(this.middleware);
...
return fn(ctx).then(handleResponse).catch(onerror);
koa
само зависит отkoa-compose
модуль, изkoa
дляfn
С точки зрения использования,middleware
должны быть заключены вfn
объект, передавcontext
объект для возвратаPromise
Теперь иди вглубьkoa-compose
module, чтобы увидеть, что он делает с нашим массивом промежуточного программного обеспечения.
// source code
return function (context, next) {
// last called middleware #
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
if (!fn) return Promise.resolve() // 递归结束
try {
return Promise.resolve(fn(context, function next () {
return dispatch(i + 1) // 递归调用dispatch
}))
} catch (err) {
return Promise.reject(err)
}
}
}
Закрытие внутри выглядит знакомо, не так ли? Сравните еще раз
// usage
app.use(async (ctx, next) => {
// 中间件函数
});
здесьPromise.resolve(fn(..))
Функции промежуточного программного обеспечения, которые помогают нам выполнять асинхронно, здесьnext
Функция объясняет, почемуKoa
Вызов промежуточного программного обеспечения выполняется рекурсивно, который рекурсивно вызываетdispatch
функция для обхода массива, в то же время все промежуточные функции используют один и тот жеctx
, еще раз рассматривая внешний
const handleRequest = (req, res) => {
res.statusCode = 404;
const ctx = this.createContext(req, res);
const onerror = err => ctx.onerror(err);
const handleResponse = () => respond(ctx);
onFinished(res, onerror);
return fn(ctx).then(handleResponse).catch(onerror);
};
return handleRequest;
context
Использовать родной узелhttp
в функции обратного вызова слушателяreq
res
для дальнейшей инкапсуляции, что означает, что для каждогоhttp
просить,koa
создастcontext
И совместно используется для всего глобального промежуточного программного обеспечения, когда все промежуточное программное обеспечение выполняется, все данные, которые должны быть возвращены, будут возвращены в унифицированныйres
Возврат, поэтому мы можем получить только из промежуточного программного обеспеченияctx
получить то, что вам нужноreq
Данные в обрабатываются, и, наконец,ctx
возвращатьbody
для родногоres
сделать возврат
Каждый запрос имеет уникальныйcontext
Объект, в него помещаются все вещи о запросе и возврате
createContext
метод будетreq
res
Дальнейшая инкапсуляция
// source code
const context = Object.create(this.context); // 创建一个对象,使之拥有context的原型方法,后面以此类推
const request = context.request = Object.create(this.request);
const response = context.response = Object.create(this.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
правила,context
должен существовать как временный объект, все должно быть заключено в объект, поэтомуapp
req
res
Родились три атрибута, но я не знаю, если у вас есть вопрос, почемуapp
req
res
также заперт вrequest
а такжеresponse
внутри ?
сделать их обоих одинаковымиapp
req
res
а такжеctx
, чтобы передать обязанности по обработке, доступ клиентов извне, им нужен толькоctx
получить всеkoa
предоставленные данные и методы, в то время какkoa
будет продолжать дальнейшее разделение этих обязанностей, таких какrequest
используется для дальнейшей инкапсуляцииreq
из,response
используется для дальнейшей инкапсуляцииres
, так что обязанности рассредоточены, связанность уменьшена, а все ресурсы разделены, чтобы всяcontext
Обладает свойством высокой сплоченности, и внутренние элементы могут иметь доступ друг к другу.
// source code
context.state = {};
Можно видеть, чтоstate
Это пустой объект, специально отвечающий за сохранение статуса отдельного запроса. Пользователи могут управлять содержимым в соответствии со своими потребностями.
// source code
const ctx = this.createContext(req, res);
const onerror = err => ctx.onerror(err);
этот огромныйctx
При его создании прослушиватель ошибок монтируется сразу в начале, но вcreateContext
не нашел этого вonerror
метод, который должен принадлежать методу-прототипу одного из модулей, после недолгих поисков я обнаружил, что он находится вcontext.js
В этом файле этот файл определяет все значения по умолчаниюcontext
объект имеет методы-прототипы
// application.js
// handleRequest()
const onerror = err => ctx.onerror(err);
onFinished(res, onerror);
return fn(ctx).then(handleResponse).catch(onerror);
Если во время выполнения асинхронной функции возникает исключение, которое не перехватывается внутри,koa
будет использоваться единообразноPromise
изcatch
оператор для обработки ошибок, если он представлен диаграммой, это
блок-схема обработки запроса koa
На данный момент нам не должно быть трудно решить вопрос в уме в начале этой статьи, поэтому мы должны унаследоватьEvents
, на самом деле использоватьnode
Встроенный прослушиватель событий для прослушивания некоторых событий, таких как известные в настоящее время события.error
, чтобы отделить функцию обработки ошибок
глобальный обработчик ошибок
// source code
// application.js
onerror(err) {
assert(err instanceof Error, `non-error thrown: ${err}`);
if (404 == err.status || err.expose) return;
if (this.silent) return;
const msg = err.stack || err.toString();
console.error();
console.error(msg.replace(/^/gm, ' '));
console.error();
}
Koa
С процессом обработки внутреннего ядра разобрались, теперь войдем во внутренний модуль, чтобы узнать
модуль запроса
Многоgetter
setter
полностью извлеченnode
http
Предоставляет полезные свойства, связанные с запросом, и некоторые необходимыеhelper
Функции здесь не повторяются, почти все функции в документации по APIctx.req
Ниже приведены свойства под ним, стоит упомянуть, чтоthis
,Головная больthis
, этот фрагмент кода использует многоthis.req
способ получить доступ к информации HTTP-запроса, предоставленной узлом изначально (потому что мы помещаем узелreq
назначенныйcontext
), когда вkoa
При наружном использовании мы используемctx.req.propertyName
в виде… да, я думаю, вы тоже разобрались, нам нужно связать нашthis
указать наctx.req
, так что вcontext.js
В модуле используется прокси дляthis
При правильной привязке упомянутый выше код присваивания, который позволяет этим модулям обращаться друг к другу, фактически в определенной степени сокращается.this
беда
// 详见 delegates 包
// https://github.com/tj/node-delegates
// Koa context.js
delegate(proto, 'request')
.method('acceptsLanguages')
.method('acceptsEncodings')
.access('query')
.access('path')
.access('url')
.getter('origin')
// delegates 的内部核心实现,通过apply来重新绑定this
proto[name] = function(){
return this[target][name].apply(this[target], arguments);
};
модуль ответа
response.js
то же самое внутриrequest.js
похожи, стоит отметить, чтоwritable
Этот геттер, если на запрос был дан ответ, возвращаетtrue
, где считанное значение равноthis.res.finished
, во многих ошибках мы часто сталкиваемся с попыткой повторно написать заголовок ответаheader
ошибка, источник этой ошибки дажеon-finished
Этот пакет может выполнять обратный вызов, чтобы сохранить состояние запроса после завершения ответа.res
вспомогательная функция ответа
koa имеет унифицированную функцию ответа, расположенную вapplication.js
в концеctx.body
ctx.status
запросить ответ
// 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);
Стоит отметить, что возвращаемый корпус поддерживаетBuffer
string
поток и наиболее распространенныйjson
полный текст
Для коммерческих перепечаток, пожалуйста, свяжитесь с автором для авторизации Для некоммерческих перепечаток, пожалуйста, укажите источник Спасибо за сотрудничество!
Контактное лицо: tecker_yuknigh@163.com