Support for 'Data' field in controllers
This commit is contained in:
parent
85f57df0cb
commit
0b04ad5e00
1 changed files with 52 additions and 5 deletions
|
@ -84,13 +84,39 @@ fn parseRouteArg(comptime T: type, segment: []const u8) !T {
|
||||||
@compileError("Unsupported Type " ++ @typeName(T));
|
@compileError("Unsupported Type " ++ @typeName(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parseBody(comptime T: type, reader: anytype, alloc: std.mem.Allocator) !T {
|
const BaseContentType = enum {
|
||||||
|
json,
|
||||||
|
url_encoded,
|
||||||
|
octet_stream,
|
||||||
|
|
||||||
|
other,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn parseBody(comptime T: type, content_type: BaseContentType, reader: anytype, alloc: std.mem.Allocator) !T {
|
||||||
const buf = try reader.readAllAlloc(alloc, 1 << 16);
|
const buf = try reader.readAllAlloc(alloc, 1 << 16);
|
||||||
defer alloc.free(buf);
|
defer alloc.free(buf);
|
||||||
|
|
||||||
|
switch (content_type) {
|
||||||
|
.octet_stream, .json => {
|
||||||
const body = try json_utils.parse(T, buf, alloc);
|
const body = try json_utils.parse(T, buf, alloc);
|
||||||
defer json_utils.parseFree(body, alloc);
|
defer json_utils.parseFree(body, alloc);
|
||||||
|
|
||||||
return try util.deepClone(alloc, body);
|
return try util.deepClone(alloc, body);
|
||||||
|
},
|
||||||
|
else => return error.UnsupportedMediaType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn matchContentType(hdr: ?[]const u8) ?BaseContentType {
|
||||||
|
if (hdr) |h| {
|
||||||
|
if (std.ascii.eqlIgnoreCase(h, "application/x-www-form-urlencoded")) return .url_encoded;
|
||||||
|
if (std.ascii.eqlIgnoreCase(h, "application/json")) return .json;
|
||||||
|
if (std.ascii.eqlIgnoreCase(h, "application/octet-stream")) return .octet_stream;
|
||||||
|
|
||||||
|
return .other;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Context(comptime Route: type) type {
|
pub fn Context(comptime Route: type) type {
|
||||||
|
@ -107,6 +133,8 @@ pub fn Context(comptime Route: type) type {
|
||||||
// leave it as a simple string instead of void
|
// leave it as a simple string instead of void
|
||||||
pub const Query = if (@hasDecl(Route, "Query")) Route.Query else void;
|
pub const Query = if (@hasDecl(Route, "Query")) Route.Query else void;
|
||||||
|
|
||||||
|
pub const Data = if (@hasDecl(Route, "Data")) Route.Data else void;
|
||||||
|
|
||||||
base_request: *http.Request,
|
base_request: *http.Request,
|
||||||
|
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
|
@ -118,6 +146,7 @@ pub fn Context(comptime Route: type) type {
|
||||||
args: Args,
|
args: Args,
|
||||||
body: Body,
|
body: Body,
|
||||||
query: Query,
|
query: Query,
|
||||||
|
data: Data,
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
body_buf: ?[]const u8 = null,
|
body_buf: ?[]const u8 = null,
|
||||||
|
@ -144,9 +173,11 @@ pub fn Context(comptime Route: type) type {
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
args: Args,
|
args: Args,
|
||||||
) !void {
|
) !void {
|
||||||
|
const base_content_type = matchContentType(req.headers.get("Content-Type"));
|
||||||
|
|
||||||
const body = if (Body != void) blk: {
|
const body = if (Body != void) blk: {
|
||||||
var stream = req.body orelse return error.NoBody;
|
var stream = req.body orelse return error.NoBody;
|
||||||
break :blk try parseBody(Body, stream.reader(), alloc);
|
break :blk try parseBody(Body, base_content_type orelse .json, stream.reader(), alloc);
|
||||||
} else {};
|
} else {};
|
||||||
defer if (Body != void) util.deepFree(alloc, body);
|
defer if (Body != void) util.deepFree(alloc, body);
|
||||||
|
|
||||||
|
@ -157,6 +188,21 @@ pub fn Context(comptime Route: type) type {
|
||||||
break :blk try query_utils.parseQuery(Query, q);
|
break :blk try query_utils.parseQuery(Query, q);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var should_free_data: bool = false;
|
||||||
|
const data = if (Data != void) blk: {
|
||||||
|
if (Query != void or Body != void) @compileError("Cannot specify both Data and Query/Body");
|
||||||
|
if (base_content_type == .url_encoded) {
|
||||||
|
const path = std.mem.sliceTo(req.uri, '?');
|
||||||
|
const q = req.uri[path.len..];
|
||||||
|
break :blk try query_utils.parseQuery(Query, q);
|
||||||
|
} else {
|
||||||
|
should_free_data = true;
|
||||||
|
var stream = req.body orelse return error.NoBody;
|
||||||
|
break :blk try parseBody(Body, base_content_type orelse .json, stream.reader(), alloc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
defer if (should_free_data) util.deepFree(alloc, data);
|
||||||
|
|
||||||
var api_conn = conn: {
|
var api_conn = conn: {
|
||||||
const host = req.headers.get("Host") orelse return error.NoHost;
|
const host = req.headers.get("Host") orelse return error.NoHost;
|
||||||
const auth_header = req.headers.get("Authorization");
|
const auth_header = req.headers.get("Authorization");
|
||||||
|
@ -184,6 +230,7 @@ pub fn Context(comptime Route: type) type {
|
||||||
.args = args,
|
.args = args,
|
||||||
.body = body,
|
.body = body,
|
||||||
.query = query,
|
.query = query,
|
||||||
|
.data = data,
|
||||||
};
|
};
|
||||||
|
|
||||||
try Route.handler(self, res, &api_conn);
|
try Route.handler(self, res, &api_conn);
|
||||||
|
|
Loading…
Reference in a new issue