Перевод документации к Vibe.d. Часть 2.
Конфигурация сервера
HTTP-сервер поддерживает некоторое количество параметров конфигурации для настройки его поведения. По умолчанию сервер будет прослушивать все локальные сетевые адаптеры на порту 80 и выполнять полный синтаксический анализ запроса. Далее предоставлен обзор наиболее распространенных настроек:
port
Порт, на котором должен прослушиваться HTTP-сервер.
bindAddresses
Список всех интерфейсов, на которых должен прослушиваться сервер. Поддерживаются адреса IPv4 и IPv6, а также имена доменов.
options
Управляет дополнительными функциями веб-сервера. Некоторые параметры могут быть отключены для увеличения скорости или уменьшения использования памяти. По умолчанию включены следующие параметры: parseURL, parseQueryString, parseFormBody, parseJsonBody, parseMultiPartBody, parseCookies.
errorPageHandler
Предоставляет способ настройки страниц ошибок.
Например:
void errorPage(HTTPServerRequest req,
HTTPServerResponse res,
HTTPServerErrorInfo error)
{
res.render!("error.dt", req, error);
}
shared static this()
{
auto settings = new HTTPServerSettings;
settings.errorPageHandler = toDelegate(&errorPage);
// ...
}
Переменные req, code и msg доступны внутри шаблона error.dt.
tlsContext
Позволяет серверу работать как сервер с HTTPS. Скорее всего, вам также придется указать порт 443, если это поле выстановлено.
HTTPS
Для обслуживания HTTPS-соединений конфигурация должна иметь TLS-окружение, имеющее соответствующий сертификат и файлы секретных ключей:
auto settings = new HTTPServerSettings;
settings.port = 443;
settings.bindAddresses = ["127.0.0.1"];
settings.tlsContext = createTLSContext(TLSContextKind.server);
settings.tlsContext.useCertificateChainFile("server-cert.pem");
settings.tlsContext.usePrivateKeyFile("server-key.pem");
Выбор поставщика TLS
В настоящее время поддерживаются два провайдера TLS: OpenSSL и порт Botan для D. По умолчанию для обеспечения функциональности TLS/HTTPS используется OpenSSL. Чтобы выбрать другого поставщика или избежать компиляции с поддержкой TLS, необходимо выбрать соответствующую конфигурацию сборки для подпакета vibe-d: tls:
dependency "vibe-d:tls" version="*" // use "notls" instead of "botan" to disable TLS support subConfiguration "vibe-d:tls" "botan"
При использовании рецепта на основе JSON, два параметра должны быть выставлены следующим образом («…» являются просто заполнителями для других возможных директив):
{
…
"dependencies": {
…
"vibe-d:tls": "*"
},
"subConfigurations": {
…
"vibe-d:tls": "botan"
}
}
Маршрутизация
Класс URLRouter предоставляет удобный способ позволить различным функциям обрабатывать разные URL-адреса. Он поддерживает статическое сопоставление пути, переменные заполнители и wild-cards (неизвестные заранее URL). Любая совпадающая переменная будет доступна как элемент словаря params.
В дополнение к пути, HTTP-метод также используется для сопоставления запросов. Каждый метод HTTP-запроса имеет соответствующий метод в классе URLRouter (например, get или post). Следующий пример направит все GET-запросы, соответствующие схеме пути “/users/*”, в обработчик userInfo и будет обслуживать все другие GET-запросы с использованием файлов в папке serveStaticFiles, см. serveStaticFiles.
Пример: маршрутизация GET / POST и статического файла:
import vibe.d;
void userInfo(HTTPServerRequest req, HTTPServerResponse res)
{
auto username = req.params["user"];
render!("userinfo.dt", username)(res);
}
void addUser(HTTPServerRequest req, HTTPServerResponse res)
{
enforceHTTP("user" in req.form, HTTPStatus.badRequest, "Missing user field.");
res.redirect("/users/"~req.form["user"]);
}
shared static this()
{
auto router = new URLRouter;
router.get("/users/:user", &userInfo);
router.post("/adduser", &addUser);
router.get("*", serveStaticFiles("./public/"));
// Чтобы уменьшить избыточность кода, можно
// использовать цепочку методов:
router
.get("/users/:user", &userInfo)
.post("/adduser", &addUser)
.get("*", serveStaticFiles("./public/"));
listenHTTP(new HTTPServerSettings, router);
}
Шаблоны Diet
Vibe.d поддерживает HTML-шаблоны с синтаксисом, по большей части совместимым с шаблонами Pug. Они обеспечивают краткий способ динамической генерации HTML-кода конечных веб-страниц. Выражения D и константы могут быть встроены в них, а полный vibe.d API доступен в шаблонах.
Шаблоны должны находиться где-то внутри папки «views» проекта. Затем они отображаются с помощью функции render, которая принимает имя файла шаблона в качестве первого аргумента шаблона, а затем список переменных, которые должны быть доступны шаблону. Наконец, для визуализации требуется HTTPServerResponse.
В следующем примере показан ряд функций компилятора шаблона Diet. Полную ссылку на синтаксис шаблона можно найти на странице шаблонизатора Diet.
Пример файла шаблона с динамическими вставками кода и несколькими другими функциями шаблона:
doctype html
html
head
title Страница: #{pageTitle}
body
h1= pageTitle
p Это контент данной страницы.
| Заголовок "#{pageTitle}" вставлен динамически.
| Вы можете использовать циклы и другие выражения D:
block placeholder
p
- foreach(i, ch; pageTitle)
| #{i+1}. символ: #{ch}
p.special.small Этот параграф имеет 'special'
| и 'small' CSS классы
p#footer Этот параграф имеет id 'footer'.
#somediv.
А это многострочный текст
внутри блока div #somediv
Страницы ошибок
Существует три способа возврата страницы ошибки клиенту:
- Исключение произошло в обработчике запроса или при разборе запроса. По умолчанию возвращается 500 «Внутренняя ошибка сервера». HTTPStatusException, код состояния можно настроить.
- Обработчик запроса не пишет ответ. В этом случае сервер автоматически возвращает ошибку 404 «Not Found».
- Обработчик запроса вручную устанавливает статус ошибки в statusCode и записывает тело. В этом случае страница с ошибкой будет точно такой, как указано в коде.
HTTPServerSettings можно использовать для предоставления настраиваемого обработчика страницы ошибок. Если он предоставлен, то вызывается для любого из первых двух условий и должен отображать страницу с ошибкой для объекта ответа. Если обработчик не указан, создается простая страница с ошибками в виде простого текста. См. пример конфигурации HTTP-сервера.
Аутентификация
В настоящее время реализованы методы аутентификации HTTP-Basic-Auth и HTTP-Digest-Auth. Механизмы более высокого уровня, такие как OAuth, будут предоставляться с использованием библиотек расширений.
Простым способом подключить аутентификацию в веб-приложении является использование сквозной функции URLRouter. Если пользователь правильно аутентифицирован, функция performBasicAuth ничего не сделает, и URLRouter будет по-прежнему соответствовать запросу ко всем следующим маршрутам. Однако если нет аутентификации или если пара имя пользователя / пароль недействительна, будет генерироваться исключение HTTPStatusException, которое сгенерирует на стороне браузера страницу ошибки 403 с диалоговым окном для ввода пароля пользователем. В таком случае маршрутизация останавливается до тех пор, пока пользователь не введет верные данные.
Обратите внимание, что при разработке веб-приложений перед началом работы рекомендуется использовать веб-фреймворк высокого уровня. См. пример веб-проекта реализации сессии, основанной на аутентификации с такой настройкой.
Пример использования HTTP-Basic-Auth для ограничения доступа:
import vibe.d;
bool checkPassword(string user, string password)
{
return user == "admin" && password == "secret";
}
shared static this()
{
auto router = new URLRouter;
// доступны следующие два маршрута без аутентификации:
router.get("/", staticTemplate!"index.dt");
router.get("/about", staticTemplate!"about.dt");
// далее любой запрос сопоставляется и проверяется для аутентификации:
router.any("*", performBasicAuth("Site Realm", toDelegate(&checkPassword)));
// следующие маршруты могут быть достигнуты только после аутентификации:
router.get("/profile", staticTemplate!"profile.dt");
router.get("/internal", staticTemplate!"internal.dt");
// ...
}
Сесии (сеансы)
HTTP-сессии на основе файлов cookie поддерживаются непосредственно HTTP-сервером. Чтобы иметь возможность использовать их, сначала необходимо установить SessionStore в настройках HTTPServerSettings. Затем сеансы устанавливаются путем вызова HTTPServerResponse.startSession и удаляются HTTPServerResponse.terminateSession. Возвращаемый объект Session ведет себя как хранилище “ключ-значение”, используя строки в качестве ключей и значений.
Пример использования HTTP-сессии для аутентификации пользователей:
import vibe.d;
void login(HTTPServerRequest req, HTTPServerResponse res)
{
enforceHTTP("username" in req.form && "password" in req.form,
HTTPStatus.badRequest, "Missing username/password field.");
// todo: проверить user/password здесь
auto session = res.startSession();
session.set("username", req.form["username"]);
session.set("password", req.form["password"]);
res.redirect("/home");
}
void logout(HTTPServerRequest req, HTTPServerResponse res)
{
if (res.session) res.terminateSession();
res.redirect("/");
}
void checkLogin(HTTPServerRequest req, HTTPServerResponse res)
{
// принудительно перенаправлять на / не аутентифицированных пользователей
if (!req.session)
res.redirect("/");
}
shared static this()
{
auto router = new URLRouter;
router.get("/", staticTemplate!"index.dt");
router.post("/login", &login);
router.post("/logout", &logout);
// ограничиваем все следующие маршруты для аутентифицированных пользователей:
router.any("*", &checkLogin);
router.get("/home", staticTemplate!"home.dt");
auto settings = new HTTPServerSettings;
settings.sessionStore = new MemorySessionStore;
// ...
}
Запросы клиентов
Клиентский запрос выполняется с использованием функции requestHTTP. Пример выполнения простого HTTP-запроса:
import vibe.vibe;
void main()
{
requestHTTP("http://google.com",
(scope HTTPClientRequest req) {
// можно добавить здесь заголовки перед отправкой,
// пишите тело POST или делайте что-то подобное здесь
},
(scope HTTPClientResponse res) {
logInfo("Ответ: %s", res.bodyReader.readAllUTF8());
}
);
}
Обратите внимание, что вы можете не указывать явные типы параметров HTTPClientRequest и HTTPClientResponse. Они будут автоматически выведены компилятором.
Пул соединений используется внутри совместно с постоянными HTTP-соединениями (keep-alive) для получения максимальной пропускной способности.