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}",
|
||||
\\ .layout = if (@hasDecl(@import("{7s}"), "layout")) @import("{7s}").layout else null,
|
||||
\\ .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,
|
||||
.template = const_route.template,
|
||||
.json_params = const_route.json_params,
|
||||
.formats = const_route.formats,
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
if (route) |capture| {
|
||||
if (!capture.validateFormat(request)) {
|
||||
return request.setResponse(try self.renderNotFound(request), .{});
|
||||
}
|
||||
}
|
||||
|
||||
switch (request.requestFormat()) {
|
||||
.HTML => try self.renderHTML(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;
|
||||
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) {
|
||||
index: ViewWithoutId,
|
||||
get: ViewWithId,
|
||||
@ -61,6 +72,7 @@ template: []const u8,
|
||||
json_params: []const []const u8,
|
||||
params: std.ArrayList(*jetzig.data.Data) = undefined,
|
||||
id: []const u8,
|
||||
formats: ?Formats,
|
||||
|
||||
/// 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()`).
|
||||
@ -101,6 +113,28 @@ pub fn match(self: Route, request: *const jetzig.http.Request) bool {
|
||||
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 {
|
||||
switch (self.view) {
|
||||
.dynamic => {},
|
||||
|
@ -23,7 +23,7 @@ pub fn main() !void {
|
||||
.index => jetzig.colors.blue("{s: <7}"),
|
||||
.post => jetzig.colors.yellow("{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}"),
|
||||
.custom => unreachable,
|
||||
};
|
||||
|
@ -37,7 +37,7 @@ const Test = struct {
|
||||
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 {
|
||||
return if (std.mem.indexOf(u8, test_fn.name, ".test.")) |index|
|
||||
|
Loading…
x
Reference in New Issue
Block a user