Обзор
Простейший пример сервера, предоставленный NODEJS, заключается в следующем:
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World!\n');
});
Фреймворк Express не такой уж и волшебный, это просто проксиhttp.createServer(requestHandler)обработчик запросов в . И используйте зарегистрированное промежуточное ПО и сопоставление маршрутов для ответа на входящие запросы пользователей.
вся идея
Читая исходный код, думаю, логику Express можно разделить на две части: запуск службы и ответ на запрос.
Начать этап обслуживанияОтноситсяhttp.createServer(requestHandler)иserver.listener()Ряд действий по инициализации, выполняемых перед вызовом двух API.
Стадия ответа на запросОтносится к обработчику события запроса, которое срабатывает, когда сервер получает запрос от клиента.
Начать этап обслуживания
Наиболее важной частью запуска службы является регистрация промежуточного программного обеспечения и маршрутов.
Можно сказать, что промежуточное ПО и маршрутизация — это функции, которые есть почти на всех серверах. В среде Express ПО промежуточного слоя и маршрутизация абстрагируются в объекты уровня.В этой статье контейнер, в котором хранятся объекты уровня ПО промежуточного слоя, называетсяобъект маршрутизатора промежуточного программного обеспечения, контейнер, в котором хранится объект слоя маршрутизации, называетсяобъект маршрутизатора маршрута.
В среде Express промежуточное ПО — это обратный вызов, который будет выполняться при совпадении пути, и маршрут должен соответствовать не только пути, но и методу http (например, get, post и т. д.). Таким образом, дляобъект маршрутизатора промежуточного программного обеспечения, обратный вызов будет выполнен сразу после сопоставления пути, нообъект маршрутизатора маршрутаОбратный вызов выполнен после согласования пути объединения какrouter.handle(req, res, next)логика внутри будет по-прежнему соответствовать методу http.
1. app.useметод
зарегистрирован лиобъект маршрутизатора промежуточного программного обеспечениявсе ещеобъект маршрутизатора маршрутамы все будем использоватьapp.use.
app.useМетод, по сути, вызывает метод использования своего собственного объекта-маршрутизатора:
var router = this._router;
fns.forEach(function (fn) {
// non-express app
if (!fn || !fn.handle || !fn.set) {
return router.use(path, fn);
}
debug('.use app under %s', path);
fn.mountpath = path;
fn.parent = this;
// restore .app property on req and res
router.use(path, function mounted_app(req, res, next) {
var orig = req.app;
fn.handle(req, res, function (err) {
setPrototypeOf(req, orig.request)
setPrototypeOf(res, orig.response)
next(err);
});
});
// mounted an app
fn.emit('mount', this);
}, this);
2. Объект маршрутизатора промежуточного программного обеспечения
Когда мы вызываем что-то вродеapp.use('/', fn)Такой оператор фактически является промежуточным программным обеспечением регистрации.
Здесь необходимо объяснить, что каждое экспресс-приложение будет использоваться при его инициализации.app.lazyrouter()Чтобы создать экземпляр объекта маршрутизатора, в этой статье мы будем называть его объектом маршрутизатора промежуточного программного обеспечения, поскольку он в основном отвечает за хранение объекта уровня промежуточного программного обеспечения, но он также может регистрировать объект маршрутизатора.Например, в процессе разработки мы будем называть форма какapp.use('/test', testRouter)Заявление.
Объект маршрутизатора промежуточного программного обеспечения поддерживает этот массив стека, который используется для загрузки объекта Layer.
Когда вызывается метод использования объекта маршрутизатора, путь и обратный вызов инкапсулируются в объект Layer и помещаются в массив стека.
Обратите внимание: маршрут объекта уровня объекта маршрутизатора промежуточного программного обеспечения не определен, что отличается от маршрута объекта уровня объекта маршрутизатора.
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: false,
end: false
}, fn);
layer.route = undefined;
this.stack.push(layer);
3. Объект маршрутизатора маршрута
Когда мы вызываем что-то вродеapp.use('/test', testRouter)Заявление может быть выражено как регистрация промежуточного программного обеспечения маршрутизации, и это промежуточное программное обеспечение выглядит следующим образом:routerфункция:
function router(req, res, next) {
router.handle(req, res, next);
}
Чтобы отличить его от объекта маршрутизатора промежуточного программного обеспечения, в этой статье промежуточное программное обеспечение маршрутизации, зарегистрированное в объекте маршрутизатора промежуточного программного обеспечения, определяется как объект маршрутизатора маршрутизации.
На данный момент я больше всего хочу вам сказать, что в экспрессе объекты маршрутизатора могут быть вложены таким образом.
Как упоминалось ранее, маршрут также будет абстрагироваться в объект уровня, аrouterФункция передается в качестве третьего параметра конструктора Layer.
4. Метод метода HTTP и экземпляр маршрута
HTTP-метод относится к методам HTTP-запроса, таким как получение, отправка, размещение, удаление и заголовок.
объект маршрутизатора маршрутаНеобходимо не только совпадать с путем, но также совпадать с методом HTTP. Функция, отвечающая за сопоставление HTTP-метода, выполняется экземпляром Route.
когда мы звонимapp[method]илиrouter[method]при звонкеrouter.routeметод (следующийthis.route(path)),следующее:
// create Router#VERB functions
methods.concat('all').forEach(function(method){
proto[method] = function(path){
var route = this.route(path)
route[method].apply(route, slice.call(arguments, 1));
return this;
};
});
router.routeМетод создаст новый объект слоя и установит обратный вызов наroute.dispatch.bind(route), что согласуется с вышеупомянутымобъект маршрутизатора промежуточного программного обеспеченияОтличается, и маршрут слоя больше не является неопределенным и, наконец, возвращает новый экземпляр маршрута. код показывает, как показано ниже:
proto.route = function route(path) {
var route = new Route(path);
var layer = new Layer(path, {
sensitive: this.caseSensitive,
strict: this.strict,
end: true
}, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);
return route;
};
Итак, какова роль возвращенного экземпляра Route? Сначала взгляните на его конструктор:
function Route(path) {
this.path = path;
this.stack = [];
debug('new %o', path)
// route handlers for various http methods
this.methods = {};
}
Экземпляр Route поддерживает массив стека, который используется для сбора объектов Layer; он также поддерживает этот объект методов, который используется для указания методов http, которым может соответствовать объект маршрута.
Объект Layer, собранный маршрутом, поддерживает реальный обратный вызов маршрута, который является следующим дескриптором:
var layer = Layer('/', {}, handle);
layer.method = method;
this.methods[method] = true;
this.stack.push(layer);
5. Слой объекта
Объект Layer поддерживает этот путь и обратный вызов, который упорядочивает путь для использования вСтадия ответа на запросЧтобы сопоставить путь, сначала посмотрите на его конструктор:
function Layer(path, options, fn) {
if (!(this instanceof Layer)) {
return new Layer(path, options, fn);
}
debug('new %o', path)
var opts = options || {};
this.handle = fn;
this.name = fn.name || '<anonymous>';
this.params = undefined;
this.path = undefined;
this.regexp = pathRegexp(path, this.keys = [], opts);
// set fast path flags
this.regexp.fast_star = path === '*'
this.regexp.fast_slash = path === '/' && opts.end === false
}
Существует три типа объектов слоя:
| Категория слоя | route | method |
|---|---|---|
| Уровень промежуточного программного обеспечения | undefined | undefined |
| Слой маршрутизации | не неопределенный | undefined |
| route Layer | undefined | не неопределенный |
Обратный вызов экземпляра слоя промежуточного слоя — fn, зарегистрированная функция промежуточного слоя; обратный вызов экземпляра уровня маршрутизации —function router(req, res, next);Все обратные вызовы экземпляра слоя маршрутаroute.dispatch.bind(route).
Стадия ответа на запрос
Начав этап обслуживания, мы завершили подготовку сервера — регистрацию промежуточного ПО и маршрутизацию.
Когда приложение выполняется дляserver.listener(), вы можете начать принимать и обрабатывать запрос клиента и, наконец, вернуть ответ сервера.
1. Улучшить объект req и объект res
Когда приходит запрос, NodeJS абстрагирует запрос в req (экземпляр http.IncomingMessage), абстрагирует ответ в res (экземпляр http.ServerResponse) и передает обработчику события запроса сервера, но в среде Express. , Объект req и объект res были улучшены.
Для расширенного содержимого обратитесь к request.js и response.js в том же каталоге, что и express.js.
Так как же он усиливается?
существуетapp.lazyrouterВ методе было добавлено промежуточное программное обеспечение, которое выглядит следующим образомmiddleware.init(this)
app.lazyrouter = function lazyrouter() {
if (!this._router) {
this._router = new Router({
caseSensitive: this.enabled('case sensitive routing'),
strict: this.enabled('strict routing')
});
this._router.use(query(this.get('query parser fn')));
this._router.use(middleware.init(this));
}
};
пока вmiddleware.init(this), вы можете видеть, что прототип req и res сбрасывается:
exports.init = function(app){
return function expressInit(req, res, next){
if (app.enabled('x-powered-by')) res.setHeader('X-Powered-By', 'Express');
req.res = res;
res.req = req;
req.next = next;
setPrototypeOf(req, app.request)
setPrototypeOf(res, app.response)
res.locals = res.locals || Object.create(null);
next();
};
};
2. ПО промежуточного слоя и маршруты для сопоставления регулярных выражений
Поскольку мы зарегистрировали промежуточное ПО и маршрутизацию на этапе запуска службы и абстрагировали их в объекты уровня, при обработке фазы запроса все становится ясно.
Основная логика такова: Пройдите через контейнер стека, поддерживаемый маршрутизатором; Для уровня промежуточного ПО (т. е. layer.route не определен) функция промежуточного ПО может быть выполнена после успешного сопоставления пути; Для уровня маршрутизации (т. е. layer.route не является неопределенным) после успешного сопоставления пути необходимо сопоставить метод http для выполнения функции маршрутизации.
В этом процессе есть следующие важные методы:
app.handle, запись экспресс-приложения для обработки запросов, по существу вызывает дескриптор собственного маршрутизатора. router.handle, пройдите массив стека, поддерживаемый маршрутизатором, чтобы найти объект уровня, который соответствует пути Route.prototype._handles_method, для объекта уровня маршрута этот метод также требуется для проверки, может ли он соответствовать методу http Route.prototype.dispatch, пройдите массив стека, поддерживаемый маршрутом, и найдите объект слоя, который соответствует пути и методу http. Layer.prototype.match, ключ к сопоставлению путей Layer.prototype.handle_request, выполнить обратный вызов после успешного совпадения
3. Механизм шаблонов
Механизм шаблонов не является оригинальным автором экспресса, но вводит другие сторонние библиотеки, а затем использует API, предоставленный сторонней библиотекой, для отображения страницы ответа и возврата ее клиенту.
В настоящее время поддерживается болееejsиpugОба шаблонизатора.
Экспресс-мозаика
Приложение Express может быть смонтировано в другом приложении Express, потому что, по сути, приложение Express должно поддерживать свой собственный объект маршрутизатора, поэтому способ монтирования фактически заключается в регистрации промежуточного программного обеспечения в родительском экспресс-приложении, промежуточного программного обеспечения. Компонент отвечает за передачу req и res в дочернее экспресс-приложение и позволяет им установить отношения родитель-потомок.Исходный код выглядит следующим образом:
// restore .app property on req and res
router.use(path, function mounted_app(req, res, next) {
var orig = req.app;
fn.handle(req, res, function (err) {
setPrototypeOf(req, orig.request)
setPrototypeOf(res, orig.response)
next(err);
});
});