Можно ли хранить текущую информацию о сеансе глобально в vibe.d? (Dlang)

Пример с сайта:

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: verify user/password here

    auto session = res.startSession();
    session["username"] = req.form["username"];
    session["password"] = req.form["password"];
    res.redirect("/home");
}

void logout(HTTPServerRequest req, HTTPServerResponse res)
{
    res.terminateSession();
    res.redirect("/");
}

void checkLogin(HTTPServerRequest req, HTTPServerResponse res)
{
    // force a redirect to / for unauthenticated users
    if( req.session is null )
        res.redirect("/");
}

shared static this()
{
    auto router = new URLRouter;
    router.get("/", staticTemplate!"index.dl");
    router.post("/login", &login);
    router.post("/logout", &logout);
    // restrict all following routes to authenticated users:
    router.any("*", &checkLogin);
    router.get("/home", staticTemplate!"home.dl");

    auto settings = new HTTPServerSettings;
    settings.sessionStore = new MemorySessionStore;
    // ...
}

Но допустим, я не хотел передавать ServerResponse через мою программу в каждую функцию. Например, что если res.session хранит идентификатор текущего пользователя. Это часто используется, поэтому я бы не хотел, чтобы это проходило через каждую функцию. Как мне сохранить эту информацию о сеансе в глобальном масштабе? Предполагая, что есть несколько пользователей, использующих сайт.

1 ответ

Решение

Это возможно, хотя и обескуражено.

Решение

Вы не можете просто сохранить его глобально, потому что нет такой вещи как "глобальный" сеанс - он всегда специфичен для контекста запроса. Даже наличие глобальных локальных потоков по умолчанию в D не помогает, так как несколько волокон совместно используют один и тот же поток выполнения.

Для таких задач vibe.d предоставляет шаблон TaskLocal, который позволяет делать именно то, что вы хотите. Я не пробовал это, но ожидаю TaskLocal!Session session "просто работать".

Пожалуйста, обратите внимание на эти два предупреждения из документов:

Однако обратите внимание, что каждая переменная TaskLocal увеличивает объем памяти любой задачи, использующей локальное хранилище задачи. Есть также издержки на доступ к переменным TaskLocal, более высокий, чем для локальных переменных потока, но, как правило, все еще O(1)

а также

Экземпляры FiberLocal ДОЛЖНЫ быть объявлены как статические / глобальные локальные переменные потока. Определение их как временной переменной / стека приведет к сбоям или повреждению данных!

возражение

Однако, основываясь на моем опыте, все же лучше явно пропустить весь такой контекст, несмотря на небольшие неудобства при печати. Использовать глобальные переменные не рекомендуется - по мере роста размера вашей программы становится неизбежно трудно отслеживать зависимости между модулями и тестировать такой код. Попадание к искушению удобства сейчас может вызвать много головной боли позже. Чтобы минимизировать лишнюю типизацию и упростить обслуживание кода, я могу предложить вместо этого определить struct Context { ...} который будет содержать указатели запроса / сеанса и будет вместо этого регулярно передаваться.

Другие вопросы по тегам