содержание
- Обзор
- пример приветствия
- Как это работает
- Несколько маршрутов, несколько обратных вызовов и промежуточное ПО
Обзор
Express — это быстрая, открытая и минималистичная среда веб-разработки, основанная на платформе Node.js. В основном включает маршрутизацию, промежуточное ПО, механизм шаблонов, обработку ошибок и другие функции.
Привет, мир, экземпляр
Добавьте 1.helloworld.js в тестовую папку.
var express = require('express');
var app = express();
app.get('/', function (req, res) {
res.end('Hello World!');
});
var server = app.listen(3000, function () {
console.log('Example app listening at 3000');
});
запустить 1.helloworlds.js
node 1.helloworls.js
Приведенный выше код запустит веб-сайт на порту 3000 этой машины, и на веб-странице будет отображаться Hello World.
Как это работает
Теперь создаем новую папку lib и пишем свою экспресс-библиотеку, чтобы понять, как она работает
YUAN-EXPRESS
|
|
| - lib
| | - application.js #包裹app层
| | - express.js #框架入口
|
| - test
| | - 1.helloworld.js
|
express.js
const Application = require('./application');
function createApplicaton() {
return new Application();
}
module.exports = createApplicaton;
Назначение: два метода app.get, app.listen в экземпляре Application.js.
Операция: создайте функцию приложения и добавьте методы получения и прослушивания в прототип.
application.js
const http = require('http')
const url = require('url')
let router = [{
path:"*",
method:"*",
handler(req,res){
res.end(`Cannot ${req.method}_${req.url}`)
}
}]
function Application() {
}
Application.prototype.get = function (path,handler) {//在Application原型上添加get方法
router.push({
path,
method: 'get',
handler
})
}
Application.prototype.listen = function () {//在Application原型上添加listen方法匹配路径,执行对应的handler方法
let self = this
const server = http.createServer(function (req,res) {
let { pathname } = url.parse(req.url,true)
for(var i = 1;i<router.length;i++){
let { path,method,handler } = router[i]
if (pathname == path && req.method.toLocaleLowerCase() == method){
return handler(req,res)
}
}
router[0].handler(req,res)
})
server.listen(...arguments)
}
module.exports = Application
Фреймворк Express построен на модуле http, встроенном в node.js.
Ключом к приведенному выше коду является метод createServer модуля http, что означает создание экземпляра HTTP-сервера. Этот метод принимает функцию обратного вызова, параметрами которой являются объект запроса и объект ответа, представляющие HTTP-запрос и HTTP-ответ соответственно.
Объект помещается в массив маршрутизатора, когда приходит запрос цикла, и когда метод запроса и путь совпадают с таковыми в объекте, выполняется метод обработчика обратного вызова.
Несколько маршрутов, несколько обратных вызовов и промежуточное ПО
- прецедент
const express = require('../lib/express');
const app = express();
/**
* 1.get是指定多个处理函数
* 2.中间件错误处理
* 3. 子路径系统 单独创建一个子路径系统,并且把它挂载到主路径 系统上
*
*/
/**
* app.use
* express.Router();
*/
app.use(function (req, res, next) {
console.log('Ware1:', Date.now());
next();
});
//路由是完整匹配的。/ != /user 所以进不来
app.get('/', function (req, res, next) {
res.end('1');
});
//创建一个新的路由容器,或者说路由系统
const user = express.Router();// router
user.use(function (req, res, next) {
console.log('Ware2', Date.now());
next();
});
//在子路径里的路径是相对于父路径
user.get('/2', function (req, res, next) {
res.end('2');
});
//use表示使用中间件,只需要匹配前缀就可以了
app.use('/user', user);//user第二个参数是处理函数 (req,res,next)
// req.url = /user/3
//app.use('/user', artcile);
app.use(function (err, req, res, next) {
res.end('catch ' + err);
});
app.listen(3000, function () {
console.log('server started at port 3000');
});
- Сначала измените структуру проекта
iExpress/
|
|
| - application.js #包裹app层
|
| - route/
| | - index.js #Router类
| | - route.js #Route类
| | - layer.js #Layer类
|
| - middle/
| | - init.js #内置中间件
|
| - test/
| | - 测试用例文件1
| | - ...
|
·- express.js #框架入口
- приложение изменено с буквального на класс приложения
- Богатые методы HTTP-запроса
- Инкапсулировать маршрутизатор
- Маршруты с одинаковым путем объединяются в группу, и вводится понятие слоя.
- Добавьте контроль маршрутизации, поддержите следующий метод и добавьте функцию захвата ошибок
- Передайте параметр out при выполнении Router.handle
- четкая логика
В тестовом коде регистрируется и добавляется несколько маршрутов, а также можно добавлять несколько методов обратного вызова, что делит логику на три этапа.
(1) Контейнер приложения распределяет метод запроса и обработчик на маршрутизатор.Когда выполняется функция слушателя прослушивания, выполняется self._router.handle(req, res, done), чтобы запустить логику, вставленную в маршрутизатор.
Класс приложения
const Router = require('./router');
Application.prototype.lazyrouter = function () {
if (!this._router) {
this._router = new Router();
}
}
methods.forEach(function (method) {
Application.prototype[method] = function () {
this.lazyrouter();
//这样写可以支持多个处理函数
this._router[method].apply(this._router, slice.call(arguments));
return this;
}
});
Application.prototype.listen = function () {
let self = this;
let server = http.createServer(function (req, res) {
function done() {//如果没有任何路由规则匹配的话会走此函数
res.end(`Cannot ${req.method} ${req.url}`);
}
//如果路由系统无法处理,也就是没有一条路由规则跟请求匹配,是会把请求交给done
self._router.handle(req, res, done);
});
server.listen(...arguments);
}
(2) Запрос каждого метода в маршрутизаторе добавит слой в текущую систему маршрутизации и создаст экземпляр маршрута в слое (уровне).
Класс маршрутизатора
proto.route = function (path) {
let route = new Route(path);
let layer = new Layer(path, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);//在Router中新增一层layer
return route;
}
methods.forEach(function (method) {
proto[method] = function (path) {//请求过来
let route = this.route(path);//往Router里添一层
route[method].apply(route, slice.call(arguments, 1));//
return this;
}
});
Если это промежуточное программное обеспечение, по умолчанию пути нет, поэтому маршрут слоя устанавливается в значение undefined.
proto.use = function (path, handler) {
if (typeof handler != 'function') {
handler = path;
path = '/';
}
let layer = new Layer(path, handler);
layer.route = undefined;//我们正是通过layer有没有route来判断是一个中间件函数还是一个路由
this.stack.push(layer);
return this
}
Когда приложение начинает прослушивать порт, выполняется метод handle маршрутизатора. добавить следующий Функция в основном отвечает за передачу управления следующему промежуточному ПО.Если текущее промежуточное ПО не завершает запрос и не вызывается следующее, запрос будет приостановлен, и промежуточное ПО, определенное позже, не будет иметь возможности быть выполненным.
Когда путь в маршрутизаторе соответствует методу, перейдите на текущий слой и запустите layer.handle_request, чтобы выполнить метод, добавленный в маршрут.
proto.handle = function (req, res, out) {
//slashAdded是否添加过/ removed指的是被移除的字符串
let idx = 0,
self = this,
slashAdded = false,
removed = '';
// /user/2
let { pathname } = url.parse(req.url, true);
function next(err) {
if (slashAdded) {
req.url = '';
slashAdded = false;
}
if (removed.length > 0) {
req.url = removed + req.url;
removed = '';
}
if (idx >= self.stack.length) {
return out(err);
}
let layer = self.stack[idx++];
//在此匹配路径 params 正则+url= req.params
if (layer.match(pathname)) {// layer.params
if (!layer.route) { //这一层是中间件层// /user/2
removed = layer.path;// /user
req.url = req.url.slice(removed.length);// /2
if (err) {
layer.handle_error(err, req, res, next);
} else {
if (req.url == '') {
req.url = '/';
slashAdded = true;
}
layer.handle_request(req, res, next);
}
} else {
if (layer.route && layer.route.handle_method(req.method)) {
//把layer的parmas属性拷贝给req.params
req.params = layer.params;
self.process_params(layer, req, res, () => {
layer.handle_request(req, res, next);
});
} else {
next(err);
}
}
} else {
next(err);
}
}
next();
}
(3) Войдите в текущий слой и выполните каждый добавленный маршрут по порядку.
Класс слоя
Layer.prototype.handle_request = function (req, res, next) {
this.handler(req, res, next);
}
Обратите внимание, что метод this.handler здесь — это route.dispatch.bind(route), добавленный при добавлении уровня. Отправка привязывается к Layer.handler в методе router.route при инициализации слоя и анализе кода отправки. :
Route.prototype.dispatch = function (req, res, out) {
let idx = 0, self = this;
function next(err) {
if (err) {//如果一旦在路由函数中出错了,则会跳过当前路由
return out(err);
}
if (idx >= self.stack.length) {
return out();//route.dispath里的out刚好是Router的next
}
let layer = self.stack[idx++];
if (layer.method == req.method.toLowerCase()) {
layer.handle_request(req, res, next);
} else {
next();
}
}
next();
}
Схема структуры текста выглядит следующим образом
Application
|
|
Router
|
| - stack
|
| - Layer
|
| - path router
|
| - method handler
Router Layer
- Обработчик маршрута уровня маршрутизатора (route.dispatch) имеет специальное свойство маршрута.
- Функция обработчика пути уровня маршрута (реальный бизнес-код) имеет специальный метод атрибута
Приложение занимается только магией упаковки и распределением маршрутизации, Реализация маршрутизатора приложение.использование, приложение.парам, приложение.получить, Инкапсуляция методов маршрутизации, таких как app.post
Логическая пояснительная схема
исходный код
Адрес склада:Ссылка на источник нажмите здесь ~