Tutorial , web development with Dlang

Dlang is a fine language to do web development.

Here a tutorial. It's going to be an application where you must login as user x and password x then you can go to calculate page which asks 2 numbers then product is returned.

Code:
pkg install zed ldc dub

Make a project
Code:
dub init test -t vibe.d

Code:
cd test
mkdir public
mkdir public/css
mkdir public/js
mkdir source
mkdir views

Create the logic
zed ./source/app.d
Code:
import vibe.vibe;
import std.conv : to;

// Step 1: Definition of the hook function. 
// It returns a boolean status string which will be injected into the method parameter.
string checkAuthentication(HTTPServerRequest req, HTTPServerResponse res)
{
    if (!req.session || !req.session.isKeySet("username")) {
        res.redirect("/index.html");
        // Throwing stops Vibe.d from executing the protected method after redirecting
        throw new HTTPStatusException(HTTPStatus.found, "Redirecting to home...");
    }
    return "authorized";
}

class WebInterface {
    // Helper to get the logged-in username safely
    private string getLoggedInUser(HTTPServerRequest req)
    {
        if (req.session && req.session.isKeySet("username")) {
            return req.session.get!string("username");
        }
        return null;
    }

    // 1. Home Page (Public - ONLY maps to /index.html)
    // Renamed to getHomeHtml to remove implicit default "/" mapping
    @path("/index.html")
    void getHomeHtml(HTTPServerRequest req, HTTPServerResponse res)
    {
        string username = getLoggedInUser(req);
        string message = "Hello from the specific index.html controller!";
        res.render!("index.dt", message, username);
    }

    // 2. Show Login Page (Public)
    @path("/login.html")
    void getLoginHtml(HTTPServerRequest req, HTTPServerResponse res, string error = null)
    {
        string username = getLoggedInUser(req);
        res.render!("login.dt", error, username);
    }

    // 3. Process Login Form (Public)
    @path("/login.html")
    @method(HTTPMethod.POST)
    void postLoginHtml(HTTPServerRequest req, HTTPServerResponse res)
    {
        string user = req.form.get("username");
        string pass = req.form.get("password");

        if (user == "x" && pass == "x") {
            auto session = res.startSession();
            session.set("username", user);
            res.redirect("/index.html");
        } else {
            getLoginHtml(req, res, "Invalid username or password.");
        }
    }

    // 4. Logout (Public)
    @path("/logout.html")
    void getLogoutHtml(HTTPServerRequest req, HTTPServerResponse res)
    {
        if (req.session) {
            res.terminateSession();
        }
        string username = null;
        res.render!("logout.dt", username);
    }

    // 5. Show the Calculation Form Page (Protected via Annotation)
    // Passes "_" as a dummy parameter name to satisfy Vibe.d syntax requirements.
    @before!checkAuthentication("_")
    @path("/calculate.html")
    void getCalculateHtml(string _, HTTPServerRequest req, HTTPServerResponse res)
    {
        string username = getLoggedInUser(req);
        res.render!("calculate.dt", username);
    }

    // 6. Process Form and Render Result (Protected via Annotation)
    @before!checkAuthentication("_")
    @path("/result.html")
    @method(HTTPMethod.POST)
    void postResultHtml(string _, HTTPServerRequest req, HTTPServerResponse res)
    {
        string username = getLoggedInUser(req);
        string x1Str = req.form.get("x1", "0");
        string x2Str = req.form.get("x2", "0");

        double val1 = x1Str.to!double;
        double val2 = x2Str.to!double;
        double calculationResult = val1 * val2;

        res.render!("result.dt", calculationResult, username);
    }
}

void main()
{
    auto settings = new HTTPServerSettings;
    settings.port = 8080;
    settings.bindAddresses = ["::1", "127.0.0.1"];
    settings.sessionStore = new MemorySessionStore;

    auto router = new URLRouter;
    auto webInterface = new WebInterface;

    // --- REGULAR WEB INTERFACE ROUTER ---
    auto webSettings = new WebInterfaceSettings;
    webSettings.urlPrefix = "/";
    router.registerWebInterface(webInterface, webSettings);

    // --- FALLBACK STATIC ASSETS ---
    router.get("*", serveStaticFiles("public/"));

    auto listener = listenHTTP(settings, router);
    scope (exit)
    {
        listener.stopListening();
    }

    logInfo("Please open http://127.0.0.1:8080 in your browser.");
    runApplication();
}

Create the views.

zed ./views/status_bar..dt
Code:
cat status_bar.dt 
hr
div.status-bar(style="background: #f0f0f0; padding: 10px; margin-top: 20px;")
    - if (username !is null)
        p(style="color: #000000; margin: 0;") Status: Logged in as #[strong(style="color: #000000;") #{username}]
    - else
        p(style="color: #000000; margin: 0;") Status: Logged out

zed ./views/index.dt
Code:
doctype html
html
    head
        title Explicit Route Example
        link(rel="stylesheet", href="/css/style.css")
    body
        h1 Hello World!
        p= message
        hr
        p
            a(href="/login.html") Login

            |  |
            a(href="/logout.html") Logout

            |  |
            a(href="/calculate.html") Go to Calculate Page
        include status_bar

zed ./views/login.dt
Code:
doctype html
html
    head
        title Login
        link(rel="stylesheet", href="/css/style.css")
    body
        h1 Login Required
        - if (error !is null)
            p(style="color: red;") #{error}
        form(action="login.html", method="POST")
            div
                label(for="username") Username:
                input(type="text", name="username", id="username", required=true)
            div
                label(for="password") Password:
                input(type="password", name="password", id="password", required=true)
            div
                button(type="submit") Login
        include status_bar

zed ./views/logout.dt
Code:
doctype html
html
    head
        title Logged Out
    body
        h1 You have been logged out.
        p Thank you for using the calculator application.
        p
            a(href="/login.html") Click here to log back in.
        include status_bar

zed ./views/calculate.dt
Code:
doctype html
html
    head
        title Calculator
        link(rel="stylesheet", href="/css/style.css")
    body
        div.nav
            a(href="/") Home


            |  | 
            a(href="/logout.html") Logout
        hr

        h1 Calculator
        label Let's calculate:

        form(action="/result.html", method="POST")
            div
                label(for="x1") Input X1:
                input(type="number", name="x1", id="x1", step="any", required=true)
            div
                label(for="x2") Input X2:
                input(type="number", name="x2", id="x2", step="any", required=true)
            div
                button(type="submit") Result

        include status_bar
zed ./views/result.dt
Code:
doctype html
html
    head
        title Calculation Result
        link(rel="stylesheet", href="/css/style.css")
    body
        div.nav
            a(href="/") Home


            |  |
            a(href="/logout.html") Logout
        hr

        h1 Calculation Complete
        p The calculated product of x1 and x2 is:
        h2= calculationResult
        hr
        a(href="/calculate.html") Calculate another number
        include status_bar


zed ./public/css/style.css
Code:
/* Reset body background and default text color */
body {
    background-color: #0b1a10; /* Very dark forest green */
    color: #4af626;            /* Retro matrix green */
    font-family: sans-serif;
    padding: 2rem;
}

/* Make all headings bright neon green */
h1, h2, h3, h4, h5, h6 {
    color: #76ff03;
    text-shadow: 0 0 10px rgba(118, 255, 3, 0.3);
}

/* Style paragraphs and generic text */
p, span, li {
    color: #a2ff86;
}

/* Style links with interactive greens */
a {
    color: #00e676;
    text-decoration: none;
    border-bottom: 1px dashed #00e676;
}

a:hover {
    color: #76ff03;
    border-bottom-style: solid;
}

/* Optional: Make containers look like a green terminal board */
div, section, article {
    border-color: #1b5e20;
}

Run :
Code:
dub run &
firefox http://127.0.0.1/index.html

THATS ALL FOLKS !
 
Back
Top