Basic Request Router

<script runat="server">
Platform.Load("core", "1.1.5");

// String() converts the CLR value so === against a literal works
var method = String(Platform.Request.Method);
var rawBody = method === "POST" ? Platform.Request.GetPostData() : "";

if (method === "GET") {
    handleGet();
} else if (method === "POST") {
    handlePost(Platform.Function.ParseJSON(rawBody + ""));
} else {
    Write(Stringify({ status: 405, statusMessage: "Method Not Allowed", error: "Method not allowed" }));
}

function handleGet() {
    var id = Platform.Request.GetQueryStringParameter("id");
    if (!id) {
        Platform.Response.ContentType = "application/json";
        Write(Stringify({ message: "Welcome to the API" }));
        return;
    }
    // String() first — a Lookup result throws on a truthiness test when the field is empty
    var record = String(Platform.Function.Lookup("Records", "data", "id", id));
    Platform.Response.ContentType = "application/json";
    if (record === "" || record === "null") {
        Write(Stringify({ status: 404, statusMessage: "Not Found", error: "Record not found" }));
    } else {
        Write(Stringify({ id: id, data: record }));
    }
}

function handlePost(body) {
    if (!body || !body.email) {
        Write(Stringify({ status: 400, statusMessage: "Bad Request", error: "email is required" }));
        return;
    }
    if (!Platform.Function.IsEmailAddress(body.email)) {
        Write(Stringify({ status: 400, statusMessage: "Bad Request", error: "Invalid email format" }));
        return;
    }
    Platform.Function.InsertData("Submissions",
        ["Email", "Name", "Timestamp"],
        [body.email, body.name || "", Platform.Function.Now()]
    );
    Platform.Response.ContentType = "application/json";
    Write(Stringify({ status: "ok", email: body.email }));
}
</script>

Show test script
<script runat="server">
/*
 * Chapter: Basic Request Router
 * Proves:
 *   1. Method coerce; 405 JSON shape for wrong method.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
Platform.Load("core", "1.1.5");
var method = String(Platform.Request.Method);
assert("method GET", method, "GET");
var body405 = Platform.Function.Stringify({ status: 405, statusMessage: "Method Not Allowed", error: "Method not allowed" });
assert("405 json", body405.indexOf("405") >= 0 ? "true" : "false", "true");
</script>

Session Management

<script runat="server">
var SESSION_COOKIE = "sfmc_session";
var SESSION_DE = "Sessions";

function dateAdd(timestamp,intervalToAdd,intervalType) {
    Platform.Variable.SetValue("@dateAdd_ts",timestamp);
    Platform.Variable.SetValue("@dateAdd_add",intervalToAdd);
    Platform.Variable.SetValue("@dateAdd_type",intervalType);
    return Platform.Function.TreatAsContent("%%=DateAdd(@dateAdd_ts, @dateAdd_add, @dateAdd_type)=%%");
}
function formatDate(dateString,dateFormat,timeFormat,isoLocale) {
    Platform.Variable.SetValue("@formatDate_string",dateString);
    Platform.Variable.SetValue("@formatDate_date",dateFormat);
    Platform.Variable.SetValue("@formatDate_time",timeFormat);
    Platform.Variable.SetValue("@formatDate_iso",isoLocale);
    return Platform.Function.TreatAsContent("%%=FormatDate(@formatDate_string, @formatDate_date, @formatDate_time, @formatDate_iso)=%%");
}

function createSession(userId, data) {
    var token = Platform.Function.GUID();
    var expires = formatDate(
        dateAdd(Platform.Function.Now(), 30, "D"),
        "MM/DD/YYYY","HH:mm:ss"
    );
    Platform.Function.InsertData(SESSION_DE,
        ["token", "userId", "data", "expires"],
        [token, userId, Stringify(data), expires]
    );
    Platform.Response.SetCookie(SESSION_COOKIE, token, expires, true);
    return token;
}

function getSession() {
    var token = Platform.Request.GetCookieValue(SESSION_COOKIE);
    if (!token) return null;

    var row = Platform.Function.LookupRows(SESSION_DE, "token", token);
    if (!row || row.length === 0) return null;

    var session = row[0];
    // Check expiry
    if (new Date(session.expires) < new Date()) {
        return null;
    }
    return {
        userId: session.userId,
        data: Platform.Function.ParseJSON(session.data + "")
    };
}

function destroySession() {
    var token = Platform.Request.GetCookieValue(SESSION_COOKIE);
    if (token) {
        Platform.Function.DeleteData(SESSION_DE, ["token"], [token]);
        Platform.Response.SetCookie(SESSION_COOKIE, "",
            "Thu, 01 Jan 1970 00:00:00 GMT", true);
    }
}

// Usage
var session = getSession();
if (!session) {
    Platform.Response.Redirect("/login", false);
}
Write("Hello, user " + session.userId);
</script>

Show test script
<script runat="server">
/*
 * Chapter: Session Management
 * Proves:
 *   1. GUID session token; SetCookie returns null.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
var token = Platform.Function.GUID();
assert("session token", ("" + token).length > 10 ? "true" : "false", "true");
var ret = Platform.Response.SetCookie("gsSessionProbe", token);
assert("SetCookie null", ret === null ? "null" : "other", "null");
</script>

Multi-Step Form

var step = parseInt(Platform.Request.GetQueryStringParameter("step") || "1", 10);
var sessionToken = Platform.Request.GetCookieValue("formSession");

// Initialize form session
if (!sessionToken) {
    sessionToken = Platform.Function.GUID();
    Platform.Response.SetCookie("formSession", sessionToken);
    Platform.Function.InsertData("FormSessions",
        ["token", "step", "data", "created"],
        [sessionToken, "1", "{}", Platform.Function.Now()]
    );
}

if (String(Platform.Request.Method) === "POST") {
    var fieldName = Platform.Request.GetFormField("fieldName");
    // Save step data
    var existing = Platform.Function.Lookup("FormSessions", "data", "token", sessionToken);
    var saved = Platform.Function.ParseJSON(existing + "");
    saved["step" + step] = { name: fieldName };
    Platform.Function.UpdateData("FormSessions",
        ["token"], [sessionToken],
        ["data", "step"],
        [Platform.Function.Stringify(saved), String(step + 1)]
    );

    if (step < 3) {
        Platform.Response.Redirect("?step=" + (step + 1), false);
    } else {
        // Final submission
        // process complete form data...
        Write("Form complete!");
    }
}
Show test script
<script runat="server">
/*
 * Chapter: Multi-Step Form
 * Proves:
 *   1. Step counter + Stringify saved state.
 * EXPECTED OUTPUT: every line starts with PASS.
 */
function assert(id, actual, expected) {
    Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
    var threw = false, msg = "";
    try { fn(); } catch (ex) { threw = true; msg = "" + ex.message; }
    Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function typeOfThunk(fn) {
    try { return "" + fn(); } catch (ex) { return "THREW:" + ("" + ex.message); }
}
var step = 1;
var saved = { name: "Ada" };
var next = String(step + 1);
assert("next step", next, "2");
assert("saved json", Platform.Function.Stringify(saved).indexOf("Ada") >= 0 ? "true" : "false", "true");
</script>

See Also