mirror of
https://github.com/ziex-dev/ziex.git
synced 2026-07-21 02:59:36 -06:00
refactor: renamte to get/set/as/delete pattern
This commit is contained in:
parent
06c8984a10
commit
4ecd96d4d8
@ -44,13 +44,13 @@ fn handleAction(ctx: zx.PageCtx(void, AuthState)) void {
|
||||
if (std.mem.eql(u8, action, "Login")) {
|
||||
const username = fd.get("username") orelse return;
|
||||
if (username.len > 0) {
|
||||
ctx.response.setCookie("session", username, .{
|
||||
ctx.response.cookies.set("session", username, .{
|
||||
.path = "/",
|
||||
.max_age = 3600, // 1 hour
|
||||
});
|
||||
}
|
||||
} else if (std.mem.eql(u8, action, "Logout")) {
|
||||
ctx.response.deleteCookie("session", .{ .path = "/" });
|
||||
ctx.response.cookies.delete("session", .{ .path = "/" });
|
||||
}
|
||||
|
||||
// Redirect back to GET to avoid form resubmission
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
pub fn UserProfile(ctx: zx.PageContext) zx.Component {
|
||||
const user_id = ctx.request.getParam("id") orelse "unknown";
|
||||
const user_id = ctx.request.params.get("id") orelse "unknown";
|
||||
|
||||
return (
|
||||
<main @allocator={ctx.arena}>
|
||||
|
||||
@ -223,7 +223,7 @@ pub fn DynamicAttr(
|
||||
|
||||
// --- Dynamic Path ---
|
||||
pub fn Page(ctx: zx.PageContext) zx.Component {
|
||||
const id = ctx.request.getParam("id");
|
||||
const id = ctx.request.params.get("id");
|
||||
return (
|
||||
<section @allocator={ctx.arena}>
|
||||
<h1>User {id}</h1>
|
||||
|
||||
@ -3,16 +3,16 @@ pub fn PUT(ctx: zx.RouteContext) !void {
|
||||
}
|
||||
|
||||
pub fn DELETE(ctx: zx.RouteContext) !void {
|
||||
ctx.response.deleteCookie("body", .{});
|
||||
ctx.response.deleteCookie("body-1", .{});
|
||||
ctx.response.cookies.delete("body", .{});
|
||||
ctx.response.cookies.delete("body-1", .{});
|
||||
try ctx.response.json(.{ .message = "Deleted" }, .{});
|
||||
}
|
||||
|
||||
// Too much column width of codes
|
||||
pub fn POST(ctx: zx.RouteContext) !void {
|
||||
const body = ctx.request.text() orelse "No body";
|
||||
ctx.response.setCookie("body", body, .{});
|
||||
ctx.response.setCookie("body-1", body, .{});
|
||||
ctx.response.cookies.set("body", body, .{});
|
||||
ctx.response.cookies.set("body-1", body, .{});
|
||||
try ctx.response.json(.{
|
||||
.message = "PST",
|
||||
.body = body,
|
||||
@ -22,8 +22,8 @@ pub fn POST(ctx: zx.RouteContext) !void {
|
||||
// Support multiple methods signature
|
||||
// pub fn POST(req: zx.Request, res: zx.Response) !void {
|
||||
// const body = req.text() orelse "No body";
|
||||
// res.setCookie("body", body, .{});
|
||||
// res.setCookie("body-1", body, .{});
|
||||
// res.cookies.set("body", body, .{});
|
||||
// res.cookies.set("body-1", body, .{});
|
||||
// try res.json(.{
|
||||
// .message = "PST",
|
||||
// .body = body,
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
var count: i32 = 0;
|
||||
pub fn Page(ctx: zx.PageContext) zx.Component {
|
||||
count += 1;
|
||||
ctx.response.setCookie("count", std.fmt.allocPrint(ctx.arena, "{d}", .{count}) catch unreachable, .{});
|
||||
ctx.response.cookies.set("count", std.fmt.allocPrint(ctx.arena, "{d}", .{count}) catch unreachable, .{});
|
||||
if (count == 4) {
|
||||
ctx.response.deleteCookie("count", .{});
|
||||
ctx.response.cookies.delete("count", .{});
|
||||
count = 0;
|
||||
}
|
||||
|
||||
const count_cookie = ctx.request.cookies.get("count");
|
||||
if (count_cookie) |cookie| {
|
||||
ctx.response.setCookie("count-old", cookie, .{});
|
||||
ctx.response.cookies.set("count-old", cookie, .{});
|
||||
}
|
||||
const allocator = ctx.arena;
|
||||
|
||||
|
||||
@ -3,12 +3,12 @@ pub fn Page(ctx: zx.PageContext) zx.Component {
|
||||
const set_username = ctx.request.searchParams.get("username") orelse "";
|
||||
var leave_username = ctx.request.searchParams.get("leave") orelse "";
|
||||
if (set_username.len > 0) {
|
||||
ctx.response.setCookie("username", set_username, .{});
|
||||
ctx.response.cookies.set("username", set_username, .{});
|
||||
chat_username = set_username;
|
||||
ctx.response.redirect("/examples/realtime", null);
|
||||
}
|
||||
if (leave_username.len > 0) {
|
||||
ctx.response.deleteCookie("username", .{});
|
||||
ctx.response.cookies.delete("username", .{});
|
||||
leave_username = leave_username;
|
||||
chat_username = "";
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ fn start(ctx: zx.RouteContext) void {
|
||||
}
|
||||
|
||||
fn end(ctx: zx.RouteContext) void {
|
||||
ctx.response.setCookie("progress", ctx.fmt("{d}", .{progress}) catch "0", .{});
|
||||
ctx.response.cookies.set("progress", ctx.fmt("{d}", .{progress}) catch "0", .{});
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// site/pages/user/[id]/page.zx
|
||||
pub fn UserProfile(ctx: zx.PageContext) zx.Component {
|
||||
const user_id = ctx.request.getParam("id") orelse "unknown";
|
||||
const user_id = ctx.request.params.get("id") orelse "unknown";
|
||||
|
||||
return (
|
||||
<main @allocator={ctx.arena}>
|
||||
|
||||
@ -590,7 +590,7 @@ fn PagesAndLayouts(allocator: zx.Allocator, props: PagesAndLayoutsProps) zx.Comp
|
||||
<li><code>app/pages/user/[id]/page.zx</code> → <code>/user/:id</code></li>
|
||||
<li><code>app/pages/blog/[slug]/page.zx</code> → <code>/blog/:slug</code></li>
|
||||
</ul>
|
||||
<p>Access the parameter value using <code>ctx.request.getParam("name")</code>:</p>
|
||||
<p>Access the parameter value using <code>ctx.request.params.get("name")</code>:</p>
|
||||
<ExampleBlock id="dynamic-route" zx_code={props.zx_dynamic} zig_code={props.zig_dynamic} html_code={props.html_dynamic} filename="user/[id]/user_profile.zx" />
|
||||
|
||||
<h3>Creating layouts</h3>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
pub fn Page(ctx: zx.PageContext) zx.Component {
|
||||
const allocator = ctx.arena;
|
||||
const slug = ctx.request.getParam("slug") orelse "unknown";
|
||||
const slug = ctx.request.params.get("slug") orelse "unknown";
|
||||
|
||||
return (
|
||||
<div @allocator={allocator}>
|
||||
|
||||
@ -486,7 +486,7 @@ pub fn Page(ctx: zx.PageContext) !zx.Component {
|
||||
<li><code>site/pages/[category]/[item]/page.zx</code> → <code>/:category/:item</code></li>
|
||||
</ul>
|
||||
<h3>Accessing Route and Query Parameters</h3>
|
||||
<p>Use <code>ctx.request.getParam("name")</code> to access dynamic route parameters and <code>ctx.request.query("name")</code> for query strings:</p>
|
||||
<p>Use <code>ctx.request.params.get("name")</code> to access dynamic route parameters and <code>ctx.request.query("name")</code> for query strings:</p>
|
||||
<ExampleBlock id="dynamic-route" zx_code={zx_example_dynamic} zig_code={zig_example_dynamic} html_code={html_example_dynamic} filename="user_profile.zx" />
|
||||
<p>Both methods return <code>?[]const u8</code>, so use <code>orelse</code> to provide a default value.</p>
|
||||
</section>
|
||||
|
||||
@ -79,7 +79,7 @@ fn frameworkBySlug(slug: []const u8) ?Framework {
|
||||
|
||||
pub fn Page(ctx: zx.PageContext) zx.Component {
|
||||
const allocator = ctx.arena;
|
||||
const slug = ctx.request.getParam("framework") orelse "";
|
||||
const slug = ctx.request.params.get("framework") orelse "";
|
||||
const maybe_framework = frameworkBySlug(slug);
|
||||
|
||||
if (maybe_framework == null) {
|
||||
|
||||
@ -87,6 +87,12 @@ cookies: Cookies = .{ .header_value = "" },
|
||||
///
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams
|
||||
searchParams: URLSearchParams = .{},
|
||||
|
||||
/// URL parameters accessor (from route matching).
|
||||
///
|
||||
/// **Zig Note:** This is an extension field not present in the web standard Request.
|
||||
/// Used for accessing dynamic route parameters (e.g., /users/:id -> params.get("id")).
|
||||
params: Params = .{},
|
||||
formdata_backend_ctx: ?*anyopaque = null,
|
||||
formdata_vtable: ?*const FormDataVTable = null,
|
||||
multiformdata_backend_ctx: ?*anyopaque = null,
|
||||
@ -125,15 +131,10 @@ vtable: ?*const VTable = null,
|
||||
pub const VTable = struct {
|
||||
/// Returns the request body as text.
|
||||
text: *const fn (ctx: *anyopaque) ?[]const u8 = &defaultText,
|
||||
/// Returns a URL parameter by name (from route matching).
|
||||
getParam: *const fn (ctx: *anyopaque, name: []const u8) ?[]const u8 = &defaultGetParam,
|
||||
|
||||
fn defaultText(_: *anyopaque) ?[]const u8 {
|
||||
return null;
|
||||
}
|
||||
fn defaultGetParam(_: *anyopaque, _: []const u8) ?[]const u8 {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// --- Instance Methods --- //
|
||||
@ -163,37 +164,10 @@ pub fn json(self: *const Request, comptime T: type, opts: std.json.ParseOptions)
|
||||
/// Returns a URL parameter by name (from route matching).
|
||||
///
|
||||
/// **Zig Note:** This is an extension method not present in the web standard Request.
|
||||
/// Used for accessing dynamic route parameters (e.g., /users/:id -> getParam("id")).
|
||||
/// Used for accessing dynamic route parameters (e.g., /users/:id -> params.get("id")).
|
||||
/// @deprecated Use `params.get(name)` instead.
|
||||
pub fn getParam(self: *const Request, name: []const u8) ?[]const u8 {
|
||||
if (self.vtable) |vt| {
|
||||
if (self.backend_ctx) |ctx| {
|
||||
return vt.getParam(ctx, name);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Returns a typed URL parameter by name (from route matching).
|
||||
///
|
||||
/// **Zig Note:** This is an extension method not present in the web standard Request.
|
||||
/// Used for accessing dynamic route parameters (e.g., /users/:id -> getParam("id")).
|
||||
pub fn getParamAs(self: *const Request, name: []const u8, comptime T: type) ?T {
|
||||
const str = self.getParam(name) orelse return null;
|
||||
|
||||
return switch (@typeInfo(T)) {
|
||||
.pointer => |p| switch (p.size) {
|
||||
.Slice => if (p.child == u8 and p.is_const) str
|
||||
else @compileError("getParamAs: only []const u8 is supported, got " ++ @typeName(T)),
|
||||
else => @compileError("getParamAs: only []const u8 is supported, got " ++ @typeName(T)),
|
||||
},
|
||||
.int => std.fmt.parseInt(T, str, 10) catch return null,
|
||||
.float => std.fmt.parseFloat(T, str) catch return null,
|
||||
.bool => if (std.mem.eql(u8, str, "true") or std.mem.eql(u8, str, "1")) true
|
||||
else if (std.mem.eql(u8, str, "false") or std.mem.eql(u8, str, "0")) false
|
||||
else return null,
|
||||
.@"enum" => std.meta.stringToEnum(T, str) orelse return null,
|
||||
else => @compileError("getParamAs: unsupported type " ++ @typeName(T)),
|
||||
};
|
||||
return self.params.get(name);
|
||||
}
|
||||
|
||||
/// Returns a FormData object representing the URL-encoded form data of the request body.
|
||||
@ -271,6 +245,50 @@ pub const URLSearchParams = struct {
|
||||
}
|
||||
};
|
||||
|
||||
// --- Params --- //
|
||||
|
||||
/// The Params interface provides utility methods to work with URL parameters.
|
||||
/// extracted from dynamic routes.
|
||||
pub const Params = struct {
|
||||
backend_ctx: ?*anyopaque = null,
|
||||
vtable: ?*const ParamsVTable = null,
|
||||
|
||||
pub const ParamsVTable = struct {
|
||||
getParam: *const fn (ctx: *anyopaque, name: []const u8) ?[]const u8 = &defaultGetParam,
|
||||
|
||||
fn defaultGetParam(_: *anyopaque, _: []const u8) ?[]const u8 {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns the value of a URL parameter by name.
|
||||
pub fn get(self: *const Params, name: []const u8) ?[]const u8 {
|
||||
if (self.vtable) |vt| {
|
||||
if (self.backend_ctx) |ctx| {
|
||||
return vt.getParam(ctx, name);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Returns a typed value of a URL parameter by name.
|
||||
pub fn as(self: *const Params, name: []const u8, comptime T: type) ?T {
|
||||
const str = self.get(name) orelse return null;
|
||||
|
||||
return switch (@typeInfo(T)) {
|
||||
.pointer => |p| switch (p.size) {
|
||||
.Slice => if (p.child == u8 and p.is_const) str else @compileError("Params.as: only []const u8 is supported, got " ++ @typeName(T)),
|
||||
else => @compileError("Params.as: only []const u8 is supported, got " ++ @typeName(T)),
|
||||
},
|
||||
.int => std.fmt.parseInt(T, str, 10) catch return null,
|
||||
.float => std.fmt.parseFloat(T, str) catch return null,
|
||||
.bool => if (std.mem.eql(u8, str, "true") or std.mem.eql(u8, str, "1")) true else if (std.mem.eql(u8, str, "false") or std.mem.eql(u8, str, "0")) false else return null,
|
||||
.@"enum" => std.meta.stringToEnum(T, str) orelse return null,
|
||||
else => @compileError("Params.as: unsupported type " ++ @typeName(T)),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// --- Headers --- //
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Headers
|
||||
|
||||
@ -343,6 +361,8 @@ pub const Builder = struct {
|
||||
cookie_header: []const u8 = "",
|
||||
search_params_ctx: ?*anyopaque = null,
|
||||
search_params_vtable: ?*const URLSearchParams.URLSearchParamsVTable = null,
|
||||
params_ctx: ?*anyopaque = null,
|
||||
params_vtable: ?*const Params.ParamsVTable = null,
|
||||
formdata_ctx: ?*anyopaque = null,
|
||||
formdata_vtable: ?*const FormDataVTable = null,
|
||||
multiformdata_ctx: ?*anyopaque = null,
|
||||
@ -370,6 +390,10 @@ pub const Builder = struct {
|
||||
.backend_ctx = self.search_params_ctx,
|
||||
.vtable = self.search_params_vtable,
|
||||
},
|
||||
.params = .{
|
||||
.backend_ctx = self.params_ctx orelse self.backend_ctx,
|
||||
.vtable = self.params_vtable,
|
||||
},
|
||||
.formdata_backend_ctx = self.formdata_ctx,
|
||||
.formdata_vtable = self.formdata_vtable,
|
||||
.multiformdata_backend_ctx = self.multiformdata_ctx,
|
||||
|
||||
@ -60,6 +60,12 @@ bodyUsed: bool = false,
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/API/Response/headers
|
||||
headers: Headers = .{},
|
||||
|
||||
/// Cookie accessor for setting/deleting cookies.
|
||||
///
|
||||
/// **Zig Note:** This is an extension field not present in the web standard Response.
|
||||
/// Used for MDN-compliant cookie management (response.cookies.set/delete).
|
||||
cookies: ResponseCookies = .{},
|
||||
|
||||
/// A boolean indicating whether the response was successful (status in the range 200–299) or not.
|
||||
///
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/API/Response/ok
|
||||
@ -236,14 +242,10 @@ pub fn redirect(self: *const Response, location: []const u8, redirect_status: ?u
|
||||
/// **Parameters:**
|
||||
/// - `name`: The cookie name.
|
||||
/// - `value`: The cookie value.
|
||||
/// - `options`: Optional cookie options (path, domain, max_age, secure, etc.).
|
||||
/// - `options`: Optional cookie options (path, domain, max_age, secure, http_only, etc.).
|
||||
/// @deprecated Use `cookies.set(name, value, options)` instead.
|
||||
pub fn setCookie(self: *const Response, name: []const u8, value: []const u8, options: ?CookieOptions) void {
|
||||
const opts = options orelse CookieOptions{};
|
||||
if (self.vtable) |vt| {
|
||||
if (self.backend_ctx) |ctx| {
|
||||
vt.setCookie(ctx, name, value, opts) catch {};
|
||||
}
|
||||
}
|
||||
self.cookies.set(name, value, options);
|
||||
}
|
||||
|
||||
/// **Extension to Web Standard:**
|
||||
@ -253,10 +255,9 @@ pub fn setCookie(self: *const Response, name: []const u8, value: []const u8, opt
|
||||
/// **Parameters:**
|
||||
/// - `name`: The cookie name to delete.
|
||||
/// - `options`: Optional cookie options (path and domain should match the original cookie).
|
||||
/// @deprecated Use `cookies.delete(name, options)` instead.
|
||||
pub fn deleteCookie(self: *const Response, name: []const u8, options: ?CookieOptions) void {
|
||||
var opts = options orelse CookieOptions{};
|
||||
opts.max_age = 0; // Setting max-age to 0 deletes the cookie
|
||||
self.setCookie(name, "", opts);
|
||||
self.cookies.delete(name, options);
|
||||
}
|
||||
|
||||
/// Gets the response writer for streaming content.
|
||||
@ -359,6 +360,33 @@ pub const Headers = struct {
|
||||
}
|
||||
};
|
||||
|
||||
// --- Cookies --- //
|
||||
|
||||
/// The ResponseCookies interface provides utility methods to work with cookies on the response.
|
||||
pub const ResponseCookies = struct {
|
||||
backend_ctx: ?*anyopaque = null,
|
||||
vtable: ?*const VTable = null,
|
||||
|
||||
/// Sets a cookie on the response.
|
||||
///
|
||||
/// https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
|
||||
pub fn set(self: *const ResponseCookies, name: []const u8, value: []const u8, options: ?CookieOptions) void {
|
||||
const opts = options orelse CookieOptions{};
|
||||
if (self.vtable) |vt| {
|
||||
if (self.backend_ctx) |ctx| {
|
||||
vt.setCookie(ctx, name, value, opts) catch {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Deletes a cookie by setting it with an expired max-age.
|
||||
pub fn delete(self: *const ResponseCookies, name: []const u8, options: ?CookieOptions) void {
|
||||
var opts = options orelse CookieOptions{};
|
||||
opts.max_age = 0;
|
||||
self.set(name, "", opts);
|
||||
}
|
||||
};
|
||||
|
||||
// --- Builder (not part of web standard) --- //
|
||||
|
||||
/// Builder for creating Response objects.
|
||||
@ -396,6 +424,10 @@ pub const Builder = struct {
|
||||
.backend_ctx = self.headers_ctx,
|
||||
.vtable = self.headers_vtable,
|
||||
},
|
||||
.cookies = .{
|
||||
.backend_ctx = self.backend_ctx,
|
||||
.vtable = self.vtable,
|
||||
},
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -87,7 +87,7 @@ pub const Cookies = struct {
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn getAs(self: Cookies, name: []const u8, comptime T: type) ?T {
|
||||
pub fn as(self: Cookies, name: []const u8, comptime T: type) ?T {
|
||||
var it = std.mem.splitScalar(u8, self.header_value, ';');
|
||||
while (it.next()) |kv| {
|
||||
const trimmed = std.mem.trimLeft(u8, kv, " ");
|
||||
@ -98,16 +98,13 @@ pub const Cookies = struct {
|
||||
|
||||
return switch (@typeInfo(T)) {
|
||||
.pointer => |p| switch (p.size) {
|
||||
.Slice => if (p.child == u8 and p.is_const) str
|
||||
else @compileError("Cookies.getAs: only []const u8 is supported, got " ++ @typeName(T)),
|
||||
.Slice => if (p.child == u8 and p.is_const) str else @compileError("Cookies.getAs: only []const u8 is supported, got " ++ @typeName(T)),
|
||||
else => @compileError("Cookies.getAs: only []const u8 is supported, got " ++ @typeName(T)),
|
||||
},
|
||||
.int => std.fmt.parseInt(T, str, 10) catch return null,
|
||||
.int => std.fmt.parseInt(T, str, 10) catch return null,
|
||||
.float => std.fmt.parseFloat(T, str) catch return null,
|
||||
.bool => if (std.mem.eql(u8, str, "true") or std.mem.eql(u8, str, "1")) true
|
||||
else if (std.mem.eql(u8, str, "false") or std.mem.eql(u8, str, "0")) false
|
||||
else return null,
|
||||
.@"enum" => std.meta.stringToEnum(T, str) orelse return null,
|
||||
.bool => if (std.mem.eql(u8, str, "true") or std.mem.eql(u8, str, "1")) true else if (std.mem.eql(u8, str, "false") or std.mem.eql(u8, str, "0")) false else return null,
|
||||
.@"enum" => std.meta.stringToEnum(T, str) orelse return null,
|
||||
.@"struct", .@"union" => @compileError("Cookies.getAs: use getJson for struct/union types"),
|
||||
else => @compileError("Cookies.getAs: unsupported type " ++ @typeName(T)),
|
||||
};
|
||||
|
||||
@ -61,6 +61,8 @@ pub fn createRequest(inner: *httpz.Request) Request {
|
||||
.cookie_header = inner.headers.get("cookie") orelse "",
|
||||
.search_params_ctx = @ptrCast(inner),
|
||||
.search_params_vtable = &search_params_vtable,
|
||||
.params_ctx = @ptrCast(inner),
|
||||
.params_vtable = ¶ms_vtable,
|
||||
.formdata_ctx = @ptrCast(inner),
|
||||
.formdata_vtable = &formdata_vtable,
|
||||
.multiformdata_ctx = @ptrCast(inner),
|
||||
@ -70,6 +72,9 @@ pub fn createRequest(inner: *httpz.Request) Request {
|
||||
|
||||
const request_vtable = Request.VTable{
|
||||
.text = &requestText,
|
||||
};
|
||||
|
||||
const params_vtable = Request.Params.ParamsVTable{
|
||||
.getParam = &requestGetParam,
|
||||
};
|
||||
|
||||
|
||||
@ -38,24 +38,25 @@ test "Request: text returns null without backend" {
|
||||
try std.testing.expect(req.text() == null);
|
||||
}
|
||||
|
||||
test "Request: getParam returns null without backend" {
|
||||
test "Request: params.get returns null without backend" {
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&buffer);
|
||||
|
||||
|
||||
const req = (Request.Builder{ .arena = fba.allocator() }).build();
|
||||
try std.testing.expect(req.getParam("id") == null);
|
||||
try std.testing.expect(req.params.get("id") == null);
|
||||
}
|
||||
|
||||
test "Request: cookies field returns Cookies accessor" {
|
||||
var buffer: [1024]u8 = undefined;
|
||||
var fba = std.heap.FixedBufferAllocator.init(&buffer);
|
||||
|
||||
|
||||
const req = (Request.Builder{
|
||||
.arena = fba.allocator(),
|
||||
.cookie_header = "session=abc123",
|
||||
.cookie_header = "session=abc123; count=42",
|
||||
}).build();
|
||||
|
||||
|
||||
try std.testing.expectEqualStrings("abc123", req.cookies.get("session").?);
|
||||
try std.testing.expectEqual(@as(i32, 42), req.cookies.@"as"("count", i32).?);
|
||||
}
|
||||
|
||||
// --- Headers --- //
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user