mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 22:16:08 +00:00
Implement response format constraints
Define `pub const formats` in a view to specify which formats are available. Use this to e.g. disable JSON responses.
This commit is contained in:
parent
68656fae35
commit
1406447f24
53
demo/src/app/views/format.zig
Normal file
53
demo/src/app/views/format.zig
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const jetzig = @import("jetzig");
|
||||||
|
|
||||||
|
// Define `pub const formats` to apply constraints to specific view functions. By default, all
|
||||||
|
// view functions respond to `json` and `html` requests. Use this feature to override those
|
||||||
|
// defaults.
|
||||||
|
pub const formats: jetzig.Route.Formats = .{
|
||||||
|
.index = &.{ .json, .html },
|
||||||
|
.get = &.{.html},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn index(request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
||||||
|
_ = data;
|
||||||
|
return request.render(.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(id: []const u8, request: *jetzig.Request, data: *jetzig.Data) !jetzig.View {
|
||||||
|
_ = data;
|
||||||
|
_ = id;
|
||||||
|
return request.render(.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "index (json)" {
|
||||||
|
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
|
||||||
|
defer app.deinit();
|
||||||
|
|
||||||
|
const response = try app.request(.GET, "/format.json", .{});
|
||||||
|
try response.expectStatus(.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "index (html)" {
|
||||||
|
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
|
||||||
|
defer app.deinit();
|
||||||
|
|
||||||
|
const response = try app.request(.GET, "/format.html", .{});
|
||||||
|
try response.expectStatus(.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "get (html)" {
|
||||||
|
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
|
||||||
|
defer app.deinit();
|
||||||
|
|
||||||
|
const response = try app.request(.GET, "/format/example-id.html", .{});
|
||||||
|
try response.expectStatus(.ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
test "get (json)" {
|
||||||
|
var app = try jetzig.testing.app(std.testing.allocator, @import("routes"));
|
||||||
|
defer app.deinit();
|
||||||
|
|
||||||
|
const response = try app.request(.GET, "/format/example-id.json", .{});
|
||||||
|
try response.expectStatus(.not_found);
|
||||||
|
}
|
3
demo/src/app/views/format/get.zmpl
Normal file
3
demo/src/app/views/format/get.zmpl
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div>
|
||||||
|
<span>Content goes here</span>
|
||||||
|
</div>
|
3
demo/src/app/views/format/index.zmpl
Normal file
3
demo/src/app/views/format/index.zmpl
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div>
|
||||||
|
<span>Content goes here</span>
|
||||||
|
</div>
|
@ -278,6 +278,7 @@ fn writeRoute(self: *Routes, writer: std.ArrayList(u8).Writer, route: Function)
|
|||||||
\\ .template = "{6s}",
|
\\ .template = "{6s}",
|
||||||
\\ .layout = if (@hasDecl(@import("{7s}"), "layout")) @import("{7s}").layout else null,
|
\\ .layout = if (@hasDecl(@import("{7s}"), "layout")) @import("{7s}").layout else null,
|
||||||
\\ .json_params = &[_][]const u8 {{ {8s} }},
|
\\ .json_params = &[_][]const u8 {{ {8s} }},
|
||||||
|
\\ .formats = if (@hasDecl(@import("{7s}"), "formats")) @import("{7s}").formats else null,
|
||||||
\\ }},
|
\\ }},
|
||||||
\\
|
\\
|
||||||
;
|
;
|
||||||
|
@ -207,6 +207,7 @@ pub fn createRoutes(
|
|||||||
.layout = const_route.layout,
|
.layout = const_route.layout,
|
||||||
.template = const_route.template,
|
.template = const_route.template,
|
||||||
.json_params = const_route.json_params,
|
.json_params = const_route.json_params,
|
||||||
|
.formats = const_route.formats,
|
||||||
};
|
};
|
||||||
|
|
||||||
try var_route.initParams(allocator);
|
try var_route.initParams(allocator);
|
||||||
|
@ -192,6 +192,12 @@ fn renderResponse(self: *Server, request: *jetzig.http.Request) !void {
|
|||||||
|
|
||||||
const route = self.matchCustomRoute(request) orelse try self.matchRoute(request, false);
|
const route = self.matchCustomRoute(request) orelse try self.matchRoute(request, false);
|
||||||
|
|
||||||
|
if (route) |capture| {
|
||||||
|
if (!capture.validateFormat(request)) {
|
||||||
|
return request.setResponse(try self.renderNotFound(request), .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (request.requestFormat()) {
|
switch (request.requestFormat()) {
|
||||||
.HTML => try self.renderHTML(request, route),
|
.HTML => try self.renderHTML(request, route),
|
||||||
.JSON => try self.renderJSON(request, route),
|
.JSON => try self.renderJSON(request, route),
|
||||||
|
@ -14,6 +14,17 @@ const StaticViewWithoutId = *const fn (*jetzig.http.StaticRequest, *jetzig.data.
|
|||||||
pub const ViewWithArgs = *const fn ([]const []const u8, *jetzig.http.Request, *jetzig.data.Data) anyerror!jetzig.views.View;
|
pub const ViewWithArgs = *const fn ([]const []const u8, *jetzig.http.Request, *jetzig.data.Data) anyerror!jetzig.views.View;
|
||||||
const StaticViewWithId = *const fn (id: []const u8, *jetzig.http.StaticRequest, *jetzig.data.Data) anyerror!jetzig.views.View;
|
const StaticViewWithId = *const fn (id: []const u8, *jetzig.http.StaticRequest, *jetzig.data.Data) anyerror!jetzig.views.View;
|
||||||
|
|
||||||
|
pub const Formats = struct {
|
||||||
|
index: ?[]const ResponseFormat = null,
|
||||||
|
get: ?[]const ResponseFormat = null,
|
||||||
|
post: ?[]const ResponseFormat = null,
|
||||||
|
put: ?[]const ResponseFormat = null,
|
||||||
|
patch: ?[]const ResponseFormat = null,
|
||||||
|
delete: ?[]const ResponseFormat = null,
|
||||||
|
custom: ?[]const ResponseFormat = null,
|
||||||
|
};
|
||||||
|
const ResponseFormat = enum { html, json };
|
||||||
|
|
||||||
pub const DynamicViewType = union(Action) {
|
pub const DynamicViewType = union(Action) {
|
||||||
index: ViewWithoutId,
|
index: ViewWithoutId,
|
||||||
get: ViewWithId,
|
get: ViewWithId,
|
||||||
@ -61,6 +72,7 @@ template: []const u8,
|
|||||||
json_params: []const []const u8,
|
json_params: []const []const u8,
|
||||||
params: std.ArrayList(*jetzig.data.Data) = undefined,
|
params: std.ArrayList(*jetzig.data.Data) = undefined,
|
||||||
id: []const u8,
|
id: []const u8,
|
||||||
|
formats: ?Formats,
|
||||||
|
|
||||||
/// Initializes a route's static params on server launch. Converts static params (JSON strings)
|
/// Initializes a route's static params on server launch. Converts static params (JSON strings)
|
||||||
/// to `jetzig.data.Data` values. Memory is owned by caller (`App.start()`).
|
/// to `jetzig.data.Data` values. Memory is owned by caller (`App.start()`).
|
||||||
@ -101,6 +113,28 @@ pub fn match(self: Route, request: *const jetzig.http.Request) bool {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return `true` if a format specification is defined for the current route/view function
|
||||||
|
/// **and** the format is supported by the current view function, otherwise return `false`.
|
||||||
|
pub fn validateFormat(self: Route, request: *const jetzig.http.Request) bool {
|
||||||
|
const formats = self.formats orelse return true;
|
||||||
|
const supported_formats = switch (self.action) {
|
||||||
|
.index => formats.index orelse return true,
|
||||||
|
.get => formats.get orelse return true,
|
||||||
|
.post => formats.post orelse return true,
|
||||||
|
.put => formats.put orelse return true,
|
||||||
|
.patch => formats.patch orelse return true,
|
||||||
|
.delete => formats.delete orelse return true,
|
||||||
|
.custom => formats.custom orelse return true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const request_format = request.requestFormat();
|
||||||
|
for (supported_formats) |supported_format| {
|
||||||
|
if ((request_format == .HTML or request_format == .UNKNOWN) and supported_format == .html) return true;
|
||||||
|
if (request_format == .JSON and supported_format == .json) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
fn renderFn(self: Route, request: *jetzig.http.Request) anyerror!jetzig.views.View {
|
fn renderFn(self: Route, request: *jetzig.http.Request) anyerror!jetzig.views.View {
|
||||||
switch (self.view) {
|
switch (self.view) {
|
||||||
.dynamic => {},
|
.dynamic => {},
|
||||||
|
@ -23,7 +23,7 @@ pub fn main() !void {
|
|||||||
.index => jetzig.colors.blue("{s: <7}"),
|
.index => jetzig.colors.blue("{s: <7}"),
|
||||||
.post => jetzig.colors.yellow("{s: <7}"),
|
.post => jetzig.colors.yellow("{s: <7}"),
|
||||||
.put => jetzig.colors.magenta("{s: <7}"),
|
.put => jetzig.colors.magenta("{s: <7}"),
|
||||||
.patch => jetzig.colors.purple("{s: <7}"),
|
.patch => jetzig.colors.bright_magenta("{s: <7}"),
|
||||||
.delete => jetzig.colors.red("{s: <7}"),
|
.delete => jetzig.colors.red("{s: <7}"),
|
||||||
.custom => unreachable,
|
.custom => unreachable,
|
||||||
};
|
};
|
||||||
|
@ -37,7 +37,7 @@ const Test = struct {
|
|||||||
trace: ?[]const u8,
|
trace: ?[]const u8,
|
||||||
};
|
};
|
||||||
|
|
||||||
const name_template = jetzig.colors.blue("{s}") ++ jetzig.colors.yellow("->") ++ "\"" ++ jetzig.colors.cyan("{s}") ++ "\" ";
|
const name_template = jetzig.colors.blue("{s}") ++ jetzig.colors.yellow("::") ++ "\"" ++ jetzig.colors.cyan("{s}") ++ "\" ";
|
||||||
|
|
||||||
pub fn init(test_fn: std.builtin.TestFn) Test {
|
pub fn init(test_fn: std.builtin.TestFn) Test {
|
||||||
return if (std.mem.indexOf(u8, test_fn.name, ".test.")) |index|
|
return if (std.mem.indexOf(u8, test_fn.name, ".test.")) |index|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user