Первоначальное намерение среды Express — расширить функции встроенных модулей Node для повышения эффективности разработки. Если копнуть глубже, Express на самом деле является абстракцией, построенной поверх встроенного в Node HTTP-модуля. Теоретически все функции, реализованные в Express, также могут быть реализованы с использованием чистого Node.
В этой статье мы рассмотрим взаимосвязь между Express и Node на основе предыдущего контента Node, включая такие понятия, как промежуточное ПО и маршрутизация. Конечно, здесь будут сделаны только некоторые обзоры, а конкретные детали будут представлены позже.
В целом Express предоставляет 4 основные функции:
- В отличие от чистого кода Node, который использует одну функцию для обработки всех запросов, Express использует «стек промежуточного программного обеспечения» для обработки потоков.
- Маршрутизация аналогична промежуточному ПО в том смысле, что обработчики вызываются только при доступе к определенному URL-адресу с помощью определенного метода HTTP.
- Методы объекта запроса и ответа были расширены.
- Модули просмотра позволяют динамически отображать и изменять содержимое HTML, а также писать HTML на других языках.
промежуточное ПО
Промежуточное ПО — одна из самых больших функций в Express. Промежуточное ПО очень похоже на нативную функцию обработки Node (принимает запрос и отвечает), но в отличие от нативного ПО промежуточного слоя разделяет процесс обработки и использует несколько функций для формирования полного потока обработки.
Мы увидим различные приложения промежуточного программного обеспечения в коде. Например, сначала используйте одно ПО промежуточного слоя для регистрации всех запросов, затем задайте заголовки HTTP в другом ПО промежуточного слоя, а затем продолжите процесс. Хотя обработка запроса также может выполняться в «большой функции», разделение задачи на несколько промежуточных программ с четкими и независимыми функциями, очевидно, больше соответствует правилам SRP в разработке программного обеспечения.
Промежуточное ПО не является уникальным для Express, такая же концепция существует в Python Django или PHP Laravel. В той же веб-инфраструктуре Ruby также есть концепция промежуточного программного обеспечения Rack.
Теперь давайте повторно реализуем приложение Hello World, используя промежуточное ПО Express. Вы обнаружите, что разработка может выполняться всего несколькими строками кода, повышая эффективность и устраняя некоторые скрытые ошибки.
Экспресс-издание Привет, мир
Сначала создайте новый проект Express: создайте новую папку и создайте в ней новый файл package.json. Вспомните, как работает package.json, в котором полностью перечислены зависимости проекта, название проекта, автор и другая информация. В нашем новом проекте package.json выглядит примерно так:
{
"name": "hello-world",
"author": "Your Name Here!",
"private": true,
"dependencies": {}
}
Затем выполните команду для установки последней версии Express и сохраните ее в package.json:
npm install express -save
После выполнения команды Express будет автоматически установлен наnode_modules, а зависимости будут явно перечислены в package.json. На данный момент содержимое package.json выглядит следующим образом:
{
"name": "hello-world",
"author": "Your Name Here!",
"private": true,
"dependencies": {
"express": "^5.0.0"
}
}
Затем скопируйте следующий код в app.js:
var express = require("express");
var http = require("http");
var app = express();
app.use(function(request, response) {
response.writeHead(200, { "Content-Type": "text/plain" });
response.end("Hello, World!");
});
http.createServer(app).listen(3000);
Во-первых, мы представили модули Express и HTTP по очереди.
Затем используйтеexpress()способ создания переменнойapp, который возвращает закрытие обработчика запросов. Это очень важно, потому что это означает, что я могу передать егоhttp.createServerметод.
Помните нативную обработку запросов Node из предыдущей главы? Это примерно так:
var app = http.createServer(function(request, response) {
response.writeHead(200, { "Content-Type": "text/plain" });
response.end("Hello, world!");
});
Эти две части кода очень похожи, закрытие обратного вызова принимает два параметра, а ответ одинаков.
Наконец, мы создали сервис и запустили его.http.createServerПринятый параметр — это функция, поэтому разумно предположить, что приложение — это просто функция, но функция представляет собой полный поток обработки промежуточного слоя в Express.
Как промежуточное ПО работает на высоком уровне
В нативном коде Node вся обработка HTTP-запросов находится в одной функции:
function requestHandler(request, response) {
console.log("In comes a request to: " + request.url);
response.end("Hello, world!");
}
Если абстрагироваться в виде блок-схемы, это будет выглядеть так:
Это не означает, что другие функции нельзя вызывать во время обработки, но все ответы на запросы отправляются этой функцией.
Промежуточное ПО использует набор функций стека промежуточного ПО для обработки этих запросов.Процесс обработки выглядит следующим образом:
Итак, теперь нам нужно понять, почему Express использует набор функций промежуточного программного обеспечения и что эти функции делают.
Теперь давайте рассмотрим предыдущий пример аутентификации пользователя: личная информация пользователя будет отображаться только в том случае, если аутентификация пройдена, и каждый запрос на доступ должен быть записан.
В этом приложении есть три промежуточных функции: запись запросов, аутентификация пользователя и отображение информации. Рабочий процесс промежуточного программного обеспечения таков: сначала запишите каждый запрос, затем выполните проверку пользователя, отобразите информацию после проверки и, наконец, ответьте на запрос. Итак, есть два возможных сценария всего рабочего процесса:
Кроме того, некоторые из этих функций промежуточного программного обеспечения должны реагировать на ответ. Если ответа нет, сервер вешает запрос, а браузер ждет.
Преимущество этого в том, что мы можем разделить приложение. Разделенные компоненты не только способствуют последующему обслуживанию, но также могут по-разному комбинироваться между компонентами.
Промежуточное ПО без каких-либо модификаций
Функция промежуточного программного обеспечения может изменять запрос и ответ, но это не обязательно. Например, предыдущий код ПО промежуточного слоя ведения журнала: ему нужно только выполнять операции ведения журнала. А чисто функциональный код промежуточной функции без каких-либо модификаций примерно такой:
function myFunMiddleware(request, response, next) {
...
nest();
}
Потому что выполнение функций промежуточного программного обеспечения идет сверху вниз. Поэтому после добавления чисто функционального промежуточного ПО для записи запросов код выглядит следующим образом:
var express = require("express");
var http = require("http");
var app = express();
// 日志记录中间件
app.use(function(request, response, next) {
console.log("In comes a " + request.method + " to " + request.url);
next();
});
// 发送实际响应
app.use(function(request, response) {
response.writeHead(200, { "Content-Type": "text/plain" });
response.end("Hello, world!");
});
http.createServer(app).listen(3000);
Изменить промежуточное ПО для запроса и ответа
Не все ПО промежуточного слоя такое же, как указано выше, и некоторые функции ПО промежуточного слоя должны обрабатывать запросы и ответы, особенно последние.
Давайте реализуем упомянутую ранее функцию промежуточного программного обеспечения проверки. Для простоты проверку может пройти только случай, когда текущая минута четная. Затем код функции промежуточного программного обеспечения выглядит примерно следующим образом:
app.use(function(request, response, next) {
console.log("In comes a " + request.method + " to " + request.url);
next();
});
app.use(function(request, response, next) {
var minute = (new Date()).getMinutes();
// 如果在这个小时的第一分钟访问,那么调用next()继续
if ((minute % 2) === 0) {
next();
} else {
// 如果没有通过验证,发送一个403的状态码并进行响应
response.statusCode = 403;
response.end("Not authorized.");
}
});
app.use(function(request, response) {
response.end('Secret info: the password is "swordfish"!'); // 发送密码信息
});
Сторонняя библиотека классов промежуточного ПО
В большинстве случаев работа, которую вы пытаетесь выполнить, вероятно, уже выполнена. То есть в сообществе уже могут быть зрелые решения для некоторых часто используемых функций. Ниже мы представим некоторые сторонние модули, обычно используемые в Express.
MORGAN: ПО промежуточного слоя для ведения журналов
MorganЭто очень мощное промежуточное ПО для журналов. Он может записывать поведение пользователя и время запроса. И это полезно для анализа необычного поведения и возможных сбоев сайта. большую часть времениMorganТакже лучший выбор для ПО промежуточного слоя ведения журналов в Express.
использовать командуnpm install morgan --saveУстановите промежуточное ПО и измените код в app.js:
var express = require("express");
var logger = require("morgan");
var http = require("http");
var app = express();
app.use(logger("short"));
app.use(function(request, response){
response.writeHead(200, {"Content-Type": "text/plain"});
response.end("Hello, world!");
});
http.createServer(app).listen(3000);
посетить сноваhttp://localhost:3000Вы увидите журнал, записанный Морганом.
ПО промежуточного слоя для статических файлов Express
Отправка статических файлов по сети — распространенный сценарий запросов для веб-приложений. Эти ресурсы обычно включают ресурсы изображений, файлы CSS и статические файлы HTML. Но простое поведение отправки файлов на самом деле требует большого количества кода, потому что есть много пограничных случаев, которые нужно проверить, и соображений производительности. И экспресс встроенныйexpress.staticМодули максимально упрощают вашу работу.
Допустим, теперь необходимоpublicПапки предоставляют файловые службы, и мы можем значительно сократить объем кода, просто используя промежуточное ПО для статических файлов:
var express = require("express");
var path = require("path");
var http = require("http");
var app = express();
var publicPath = path.resolve(__dirname, "public");
app.use(express.static(publicPath));
app.use(function(request, response) {
response.writeHead(200, { "Content-Type": "text/plain" });
response.end("Looks like you didn't find a static file.");
});
http.createServer(app).listen(3000);
Теперь любойpublicСтатические файлы в каталоге могут быть запрошены напрямую, поэтому вы можете поместить все нужные вам файлы в этот каталог. еслиpublicВ папке нет подходящих файлов, он перейдет к следующему промежуточному программному обеспечению и ответит сегментомнет подходящих файловИнформация.
Зачем использовать path.resolve? Почему бы не использовать его напрямую/publicЭто связано с тем, что каталоги в Mac и Linux/publicтогда как Windows использует обратную косую черту\public. path.resolve используется для решения проблемы пути к каталогу на нескольких платформах.
больше промежуточного ПО
Помимо промежуточного ПО Morgan и статического промежуточного ПО Express, описанных выше, существует множество других мощных промежуточных программ, таких как:
- connect-ratelimit: позволяет контролировать количество подключений в час. Если кто-то делает много запросов к сервису, вы можете просто вернуть ошибку, чтобы прекратить обработку этих запросов.
- шлем: HTTP-заголовки могут быть добавлены для борьбы с некоторыми сетевыми атаками. Конкретное содержание будет рассмотрено в главе о безопасности позже.
- cookie-parses: используется для анализа информации о файлах cookie в браузере.
- время отклика: отправляя информацию о времени отклика X, вы можете лучше отлаживать производительность своего приложения.
маршрутизация
Маршрутизация — это метод сопоставления URL-адресов и методов HTTP с конкретными функциями обратного вызова. Предполагая, что в проекте есть домашняя страница, страница о нас и страница 404, давайте посмотрим, как отображаются маршруты:
var express = require("express");
var path = require("path");
var http = require("http");
var app = express();
// 像之前一样设置静态文件中间件。
// 所有的请求通过这个中间件,如果没有文件被找到的话会继续前进
var publicPath = path.resolve(__dirname, "public");
app.use(express.static(publicPath));
// 当请求根目录的时候被调用
app.get("/", function(request, response) {
response.end("Welcome to my homepage!");
});
// 当请求/about的时候被调用
app.get("/about", function(request, response) {
response.end("Welcome to the about page!");
});
// 当请求/weather的时候被调用
app.get("/weather", function(request, response) {
response.end("The current weather is NICE.");
});
// 前面都不匹配,则路由错误。返回 404 页面
app.use(function(request, response) {
response.statusCode = 404;
response.end("404");
});
http.createServer(app).listen(3000);
В приведенном выше коде, в дополнение к упомянутому выше промежуточному программному обеспечению, последние триapp.getФункции — это мощная система маршрутизации в Express. Они используют app.post для ответа на все сетевые запросы, такие как POST или PUT. Первый параметр в функции — это путь, такой как /about или /weather, или просто корневой каталог / , а второй параметр — функция обработчика запроса. Этот обработчик работает так же, как и предыдущее промежуточное ПО, единственное отличие — время вызова.
В дополнение к фиксированным формам маршрутизации он также может соответствовать более сложной маршрутизации (с использованием регулярных выражений и т. д.):
// 指定“hello”为路由的固定部分
app.get("/hello/:who", function(request, response) {
// :who 并不是固定住,它表示 URL 中传递过来的名字
response.end("Hello, " + request.params.who + ".");
});
Перезапустите службу и получите доступlocalhost:3000/hello/earthИнформация об ожидающем ответе:
Hello, earth
Обратите внимание, что если вы вставите несколько/слова, например:localhost:3000/hello/entire/earthвернет ошибку 404.
Вы должны были видеть такие URL-ссылки в своей повседневной жизни, конкретный пользователь может получить доступ к определенному URL-адресу. Например, пользовательExpressSuperHero, то URL страницы его профиля может быть таким:
В Express вы можете упростить определения маршрутов, используя этот метод подстановки, без необходимости перечисления всех пользовательских маршрутов.
В официальной документации также приведен пример использования регулярных выражений для сложного сопоставления, и с маршрутизацией можно делать еще много чего. Но в этой главе нужно знать только концепцию маршрутизации, более подробное содержание будет объяснено в главе 5.
Расширить запрос и ответ
Экспресс расширяет функциональность объектов запроса и ответа на основе оригинала. можно найти в официальномДокументацияНайдите все подробности в , но давайте сначала взглянем на некоторые из них:
Среди функций, предоставляемых Express, перенаправление — очень хорошая функция, использование которой выглядит следующим образом:
response.redirect("/hello/world");
response.redirect("http://expressjs.com");
В нативном Node нет метода перенаправления. Хотя мы также можем реализовать функцию перенаправления с помощью нативного кода, очевидно, что это будет больше кода.
Кроме того, отправка файлов в Express также стала проще благодаря всего одной строке кода:
response.sendFile("path/to/cool_song.mp3")
Как и прежде, собственный код реализации этой функции сложен.
В дополнение к расширению ответа объекта ответа Express также расширяет запрос объекта запроса. Например: вы можете пройтиrequest.ipПолучить IP-адрес машины, отправляющей запрос или парольrequest.getПолучить заголовки HTTP.
Ниже мы используем его для реализации функции черного списка IP, код выглядит следующим образом:
var express = require("express");
var app = express();
var EVIL_IP = "123.45.67.89";
app.use(function(request, response, next) {
if (request.ip === EVIL_IP) {
response.status(401).send("Not allowed!");
} else {
next();
}
});
...
используется здесьreq.ipа такжеres.status()иres.send(), и все эти методы являются расширениями Express.
Теоретически нам достаточно знать, что Express расширяет запрос и ответ, и знать, как им пользоваться, а детали можно игнорировать.
Приведенный выше пример — это только вершина айсберга всех расширений Express, вы можетеДокументацияСмотрите больше примеров в .
Посмотреть
Почти весь контент веб-сайта отображается на основе HTML, и в большинстве случаев этот HTML-контент генерируется динамически. Возможно, вам потребуется предоставить определенную страницу приветствия для текущего пользователя, вошедшего в систему, или вам может потребоваться динамически создать таблицу данных на странице. Для того, чтобы справиться с отрисовкой динамического контента, в сообществе появилось большое количество движков шаблонов Express, таких как: EJS, Handlebars, Pug.
Ниже приведен пример использования механизма шаблонов EJS:
var express = require("express");
var path = require("path");
var app = express();
// 告诉 Express 你的视图存在于一个名为 views 的文件夹中
app.set("views", path.resolve(__dirname, "views"));
// 告诉 Express 你将使用EJS模板引擎
app.set("view engine", "ejs");
В коде сначала импортируем нужные модули. Затем укажите путь, по которому находится файл представления. Затем мы устанавливаем механизм шаблонов на EJS (Документация). Конечно, при использовании выполнения EJS нам также нужно передатьnpm install ejs --saveкоманда для установки.
После установки и настройки движка EJS возникает следующий вопрос, как им пользоваться.
Во-первых, мыviewsСоздайте папку подindex.ejsфайл и скопируйте следующее:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello, world!</title>
</head>
<body>
<%= message %>
</body>
</html>
EJS, по сути, представляет собой надмножество HTML, и все синтаксис HTML можно использовать напрямую, и они полностью совместимы. Но EJS частично расширяет синтаксис. Например, вы можете пройти<%= message %>Параметры, которые будет передавать грамматикаmessageВставьте в этикетку.
app.get("/", function(request, response) {
response.render("index", {
message: "Hey everyone! This is my webpage."
});
});
Express добавляет к объекту ответа функцию, называемуюrenderМетоды. Этот метод ищет файл представления шаблона, соответствующий первому параметру в каталоге представления, и передает второй параметр в файл шаблона.
Ниже приведено содержимое HTML-файла, динамически сгенерированного движком рендеринга:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello, world!</title>
</head>
<body>
Hey everyone! This is my webpage.
</body>
</html>
Пример: Реализация доски объявлений
В этой заключительной части мы будем использовать предыдущие методы для создания полного веб-приложения доски объявлений. В этом примере, чтобы углубить понимание вышеуказанного контента, приложение в основном состоит из двух страниц:
- Домашняя страница: в основном используется для перечисления всех предыдущих сообщений
- Страница редактирования: для редактирования новых комментариев
Готов к работе
Во-первых, мы создаем новую папку и создаем новый проект и копируем следующее содержимое в новый.package.jsonВ файле:
{
"name": "express-guestbook",
"private": true,
"scripts": {
"start": "node app"
}
}
Вы можете добавить в файл другую информацию о полях (например, об авторе или версии), но в данном случае это не обязательно. Далее устанавливаем файлы зависимостей и вводим команду:
npm install express morgan body-parser ejs --save
Поскольку необходимо реализовать действие по созданию сообщения, необходимо использоватьbody-parserРазбирать POST-запросы.
основной код
После того, как подготовка завершена, следующим шагом будет созданиеapp.jsфайл и скопируйте код ниже:
var http = require("http");
var path = require("path");
var express = require("express");
var logger = require('morgan');
var bodyParser = require("body-parser");
var app = express();
// 设置引擎
app.set("views", path.resolve(__dirname, "views"));
app.set("view engine", "ejs");
// 设置留言的全局变量
var entries = [];
app.locals.entries = entries;
// 使用 Morgan 进行日志记录
app.use(logger("dev"));
// 设置用户表单提交动作信息的中间件,所有信息会保存在 req.body 里
app.use(bodyParser.urlencoded({ extended: false }));
// 当访问了网站根目录,就渲染主页(位于views/index.ejs)
app.get("/", function(request, response) {
response.render("index");
});
// 渲染“新留言”页面(位于views/index.ejs)当get访问这个URL的时候
app.get("/new-entry", function(request, response) {
response.render("new-entry");
});
// POST 动作进行留言新建的路由处理
app.post("/new-entry", function(request, response) {
// 如果用户提交的表单没有标题或者内容,则返回一个 400 的错误
if (!request.body.title || !request.body.body) {
response.status(400).send("Entries must have a title and a body.");
return;
}
// 添加新留言到 entries 中
entries.push({
title: request.body.title,
content: request.body.body,
published: new Date()
});
// 重定向到主页来查看你的新条目
response.redirect("/");
});
// 渲染404页面,因为你请求了未知资源
app.use(function(request, response) {
response.status(404).render("404");
});
// 在3000端口启动服务器
http.createServer(app).listen(3000, function() {
console.log("Guestbook app started on port 3000.");
});
Новый вид
Наконец, нам нужно завершить просмотр файла страницы и создать новый.viewsпапку, а затем скопируйте следующее содержимое в новуюheader.ejsВ файле:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Express Guestbook</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
</head>
<body class="container">
<h1>
Express Guestbook
<a href="/new-entry" class="btn btn-primary pull-right">
Write in the guestbook
</a>
</h1>
Здесь используется фреймворк Twitter Bootstrap, но, конечно, вы можете делать любые замены. Самое главное, что этот файл будет использоваться как общий заголовок для всех страниц.
Затем создайте новый в том же каталогеfooter.ejsКак общий нижний колонтитул:
</body>
</html>
После того, как общая часть сделана, следующий шагindex,new-entry,404Файл подкачки. Скопируйте приведенный ниже код в файлviews/index.ejsсередина:
<% include header %>
<% if (entries.length) { %>
<% entries.forEach(function(entry) { %>
<div class="panel panel-default">
<div class="panel-heading">
<div class="text-muted pull-right">
<%= entry.published %>
</div>
<%= entry.title %>
</div>
<div class="panel-body">
<%= entry.content %>
</div>
</div>
<% }) %>
<% } else { %>
No entries! <a href="/new-entry">Add one!</a>
<% } %>
<% include footer %>
Также скопируйте приведенный ниже код вviews/new-entry.ejsсередина
<% include header %>
<h2>Write a new entry</h2>
<form method="post" role="form">
<div class="form-group">
<label for="title">Title</label>
<input type="text" class="form-control" id="title" name="title" placeholder="Entry title" required>
</div>
<div class="form-group">
<label for="content">Entry text</label>
<textarea class="form-control" id="body" name="body" placeholder="Love Express! It's a great tool for building websites." rows="3" required></textarea>
</div>
<div class="form-group">
<input type="submit" value="Post entry" class="btn btn-primary">
</div>
</form>
<% include footer %>
Ну наконец тоviews/404.ejsфайл тоже:
<% include header %>
<h2>404! Page not found.</h2>
<% include footer %>
Все файлы представления созданы, следующим шагом является запуск службы.
запустить службу
если вы используете его сейчасnpm startПоднимите сервис, а затем перейдите по соответствующему URL-адресу, вы можете увидеть сцену, показанную на рисунке ниже.
Наконец, давайте рассмотрим несколько ключевых моментов этого небольшого проекта:
- Промежуточное программное обеспечение используется для регистрации всех запросов и ответов 404 страниц для несопоставленных URL-ссылок.
- После создания нового комментария мы перенаправили страницу на главную.
- В этом проекте EJS используется в качестве механизма шаблонов Express. И используйте его для достижения динамического рендеринга файлов HTML.
Суммировать
- Компания Express провела инженерное расширение на базе Node, сделав процесс разработки более плавным и эффективным.
- Экспресс в основном состоит из четырех частей.
- Поток обработки запросов Express может быть построен с использованием нескольких промежуточных программ.
- Популярным механизмом шаблонов в Express является EJS, который обеспечивает динамическую визуализацию HTML и более удобный синтаксис.
оригинальныйадрес