Перевод документации к 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) для получения максимальной пропускной способности.