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));
|
||||
}
|
||||
|
||||
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);
|
||||
defer alloc.free(buf);
|
||||
|
||||
switch (content_type) {
|
||||
.octet_stream, .json => {
|
||||
const body = try json_utils.parse(T, buf, alloc);
|
||||
defer json_utils.parseFree(body, alloc);
|
||||
|
||||
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 {
|
||||
|
@ -107,6 +133,8 @@ pub fn Context(comptime Route: type) type {
|
|||
// leave it as a simple string instead of 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,
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
|
@ -118,6 +146,7 @@ pub fn Context(comptime Route: type) type {
|
|||
args: Args,
|
||||
body: Body,
|
||||
query: Query,
|
||||
data: Data,
|
||||
|
||||
// TODO
|
||||
body_buf: ?[]const u8 = null,
|
||||
|
@ -144,9 +173,11 @@ pub fn Context(comptime Route: type) type {
|
|||
alloc: std.mem.Allocator,
|
||||
args: Args,
|
||||
) !void {
|
||||
const base_content_type = matchContentType(req.headers.get("Content-Type"));
|
||||
|
||||
const body = if (Body != void) blk: {
|
||||
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 {};
|
||||
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);
|
||||
};
|
||||
|
||||
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: {
|
||||
const host = req.headers.get("Host") orelse return error.NoHost;
|
||||
const auth_header = req.headers.get("Authorization");
|
||||
|
@ -184,6 +230,7 @@ pub fn Context(comptime Route: type) type {
|
|||
.args = args,
|
||||
.body = body,
|
||||
.query = query,
|
||||
.data = data,
|
||||
};
|
||||
|
||||
try Route.handler(self, res, &api_conn);
|
||||
|
|
Loading…
Reference in a new issue