mirror of
https://github.com/jetzig-framework/jetzig.git
synced 2025-05-14 14:06:08 +00:00
Error handling
This commit is contained in:
parent
65bb626bbc
commit
cb52ccffb9
@ -18,10 +18,13 @@ If you are interested in _Jetzig_ you will probably find these tools interesting
|
||||
* :white_check_mark: _JSON_-compatible response data builder.
|
||||
* :white_check_mark: _HTML_ templating (see [Zmpl](https://github.com/bobf/zmpl)).
|
||||
* :white_check_mark: Per-request arena allocator.
|
||||
* :x: Sessions.
|
||||
* :x: Cookies.
|
||||
* :x: Headers.
|
||||
* :white_check_mark: Sessions.
|
||||
* :white_check_mark: Cookies.
|
||||
* :x: Error handling.
|
||||
* :x: Headers (available but not yet wrapped).
|
||||
* :x: Param/JSON payload parsing/abstracting.
|
||||
* :x: Development-mode responses for debugging.
|
||||
* :x: Environment configurations (develompent/production/etc.)
|
||||
* :x: Middleware extensions (for e.g. authentication).
|
||||
* :x: Email delivery.
|
||||
* :x: Custom/dynamic routes.
|
||||
|
@ -22,7 +22,7 @@
|
||||
// the new URL.
|
||||
//
|
||||
.url = "https://github.com/bobf/zmpl/archive/refs/tags/0.0.1.tar.gz",
|
||||
.hash = "12205f95df0bf7a66c9d00ed76f8c3b1548eb958f9210425045c77f88ea165f20fef",
|
||||
.hash = "12204256376f262a58935d66a2a0b41ac0447299b7e63a4c6ff160ddcef6572cd3c7",
|
||||
|
||||
// This is computed from the file contents of the directory of files that is
|
||||
// obtained after fetching `url` and applying the inclusion rules given by
|
||||
|
@ -6,7 +6,9 @@ const Data = jetzig.data.Data;
|
||||
const View = jetzig.views.View;
|
||||
|
||||
pub fn index(request: *Request, data: *Data) anyerror!View {
|
||||
_ = request;
|
||||
var object = try data.object();
|
||||
try object.put("foo", data.string("hello"));
|
||||
return request.render(.ok);
|
||||
return error.OhNo;
|
||||
// return request.render(.ok);
|
||||
}
|
||||
|
@ -12,8 +12,12 @@ pub fn put(id: []const u8, request: *Request, data: *Data) anyerror!View {
|
||||
|
||||
const count = try request.session.get("count");
|
||||
if (count) |value| {
|
||||
try request.session.put("count", data.integer(value.integer.value + 1));
|
||||
try object.put("count", data.integer(value.integer.value + 1));
|
||||
if (value == .integer) {
|
||||
try request.session.put("count", data.integer(value.integer.value + 1));
|
||||
try object.put("count", data.integer(value.integer.value + 1));
|
||||
} else {
|
||||
return error.InvalidSessionData;
|
||||
}
|
||||
} else {
|
||||
try request.session.put("count", data.integer(0));
|
||||
try object.put("count", data.integer(0));
|
||||
@ -27,7 +31,11 @@ pub fn get(id: []const u8, request: *Request, data: *Data) anyerror!View {
|
||||
var object = try data.object();
|
||||
const count = try request.session.get("count");
|
||||
if (count) |value| {
|
||||
try object.put("count", data.integer(value.integer.value + 1));
|
||||
if (value == .integer) {
|
||||
try object.put("count", data.integer(value.integer.value + 1));
|
||||
} else {
|
||||
try object.put("count", data.integer(0));
|
||||
}
|
||||
} else {
|
||||
try object.put("count", data.integer(0));
|
||||
}
|
||||
|
@ -36,8 +36,12 @@ pub fn init(allocator: std.mem.Allocator) !App {
|
||||
}
|
||||
};
|
||||
|
||||
const logger = loggers.Logger{ .development_logger = loggers.DevelopmentLogger.init(allocator) };
|
||||
var logger = loggers.Logger{ .development_logger = loggers.DevelopmentLogger.init(allocator) };
|
||||
const secret = try generateSecret(allocator);
|
||||
logger.debug(
|
||||
"Running in development mode, using auto-generated cookie encryption key:\n {s}",
|
||||
.{secret},
|
||||
);
|
||||
|
||||
const server_options = http.Server.ServerOptions{
|
||||
.cache = server_cache,
|
||||
|
@ -68,8 +68,8 @@ fn processRequests(self: *Self) !void {
|
||||
while (response.reset() != .closing) {
|
||||
self.processNextRequest(&response) catch |err| {
|
||||
switch (err) {
|
||||
error.EndOfStream => continue,
|
||||
error.ConnectionResetByPeer => continue,
|
||||
error.EndOfStream, error.ConnectionResetByPeer => continue,
|
||||
error.UnknownHttpMethod => continue, // TODO: Render 400 Bad Request here ?
|
||||
else => return err,
|
||||
}
|
||||
};
|
||||
@ -100,8 +100,7 @@ fn processNextRequest(self: *Self, response: *std.http.Server.Response) !void {
|
||||
try response.headers.append("Set-Cookie", header);
|
||||
}
|
||||
response.status = switch (result.value.status_code) {
|
||||
.ok => .ok,
|
||||
.not_found => .not_found,
|
||||
inline else => |status_code| @field(std.http.Status, @tagName(status_code)),
|
||||
};
|
||||
|
||||
try response.do();
|
||||
@ -148,9 +147,12 @@ fn renderHTML(
|
||||
// FIXME: Tidy this up and use a hashmap for templates (or a more comprehensive
|
||||
// matching system) instead of an array.
|
||||
if (std.mem.eql(u8, expected_name, template.name)) {
|
||||
const view = try matched_route.render(matched_route, request);
|
||||
const content = try template.render(view.data);
|
||||
return .{ .allocator = self.allocator, .content = content, .status_code = .ok };
|
||||
const rendered = try self.renderView(matched_route, request, template);
|
||||
return .{
|
||||
.allocator = self.allocator,
|
||||
.content = rendered.content,
|
||||
.status_code = rendered.view.status_code,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -174,12 +176,15 @@ fn renderJSON(
|
||||
route: ?jetzig.views.Route,
|
||||
) !jetzig.http.Response {
|
||||
if (route) |matched_route| {
|
||||
const view = try matched_route.render(matched_route, request);
|
||||
var data = view.data;
|
||||
const rendered = try self.renderView(matched_route, request, null);
|
||||
var data = rendered.view.data;
|
||||
|
||||
if (data.value) |_| {} else _ = try data.object();
|
||||
|
||||
return .{
|
||||
.allocator = self.allocator,
|
||||
.content = try data.toJson(),
|
||||
.status_code = .ok,
|
||||
.status_code = rendered.view.status_code,
|
||||
};
|
||||
} else return .{
|
||||
.allocator = self.allocator,
|
||||
@ -188,10 +193,42 @@ fn renderJSON(
|
||||
};
|
||||
}
|
||||
|
||||
const RenderedView = struct { view: jetzig.views.View, content: []const u8 };
|
||||
fn renderView(
|
||||
self: *Self,
|
||||
matched_route: jetzig.views.Route,
|
||||
request: *jetzig.http.Request,
|
||||
template: ?jetzig.TemplateFn,
|
||||
) !RenderedView {
|
||||
const view = matched_route.render(matched_route, request) catch |err| {
|
||||
switch (err) {
|
||||
error.OutOfMemory => return err,
|
||||
else => return try self.internalServerError(request, err),
|
||||
}
|
||||
};
|
||||
const content = if (template) |capture| try capture.render(view.data) else "";
|
||||
|
||||
return .{ .view = view, .content = content };
|
||||
}
|
||||
|
||||
fn internalServerError(self: *Self, request: *jetzig.http.Request, err: anyerror) !RenderedView {
|
||||
_ = self;
|
||||
request.response_data.reset();
|
||||
var object = try request.response_data.object();
|
||||
try object.put("error", request.response_data.string(@errorName(err)));
|
||||
return .{
|
||||
.view = jetzig.views.View{ .data = request.response_data, .status_code = .internal_server_error },
|
||||
.content = "An unexpected error occurred.",
|
||||
};
|
||||
}
|
||||
|
||||
fn requestLogMessage(self: *Self, request: *jetzig.http.Request, result: jetzig.caches.Result) ![]const u8 {
|
||||
const status: jetzig.http.status_codes.TaggedStatusCode = switch (result.value.status_code) {
|
||||
.ok => .{ .ok = .{} },
|
||||
.not_found => .{ .not_found = .{} },
|
||||
inline else => |status_code| @unionInit(
|
||||
jetzig.http.status_codes.TaggedStatusCode,
|
||||
@tagName(status_code),
|
||||
.{},
|
||||
),
|
||||
};
|
||||
|
||||
const formatted_duration = try jetzig.colors.duration(self.allocator, self.duration());
|
||||
|
@ -3,8 +3,67 @@ const std = @import("std");
|
||||
const jetzig = @import("../../jetzig.zig");
|
||||
|
||||
pub const StatusCode = enum {
|
||||
@"continue",
|
||||
switching_protocols,
|
||||
processing,
|
||||
early_hints,
|
||||
ok,
|
||||
created,
|
||||
accepted,
|
||||
non_authoritative_info,
|
||||
no_content,
|
||||
reset_content,
|
||||
partial_content,
|
||||
multi_status,
|
||||
already_reported,
|
||||
im_used,
|
||||
multiple_choice,
|
||||
moved_permanently,
|
||||
found,
|
||||
see_other,
|
||||
not_modified,
|
||||
use_proxy,
|
||||
temporary_redirect,
|
||||
permanent_redirect,
|
||||
bad_request,
|
||||
unauthorized,
|
||||
payment_required,
|
||||
forbidden,
|
||||
not_found,
|
||||
method_not_allowed,
|
||||
not_acceptable,
|
||||
proxy_auth_required,
|
||||
request_timeout,
|
||||
conflict,
|
||||
gone,
|
||||
length_required,
|
||||
precondition_failed,
|
||||
payload_too_large,
|
||||
uri_too_long,
|
||||
unsupported_media_type,
|
||||
range_not_satisfiable,
|
||||
expectation_failed,
|
||||
misdirected_request,
|
||||
unprocessable_entity,
|
||||
locked,
|
||||
failed_dependency,
|
||||
too_early,
|
||||
upgrade_required,
|
||||
precondition_required,
|
||||
too_many_requests,
|
||||
request_header_fields_too_large,
|
||||
unavailable_for_legal_reasons,
|
||||
internal_server_error,
|
||||
not_implemented,
|
||||
bad_gateway,
|
||||
service_unavailable,
|
||||
gateway_timeout,
|
||||
http_version_not_supported,
|
||||
variant_also_negotiates,
|
||||
insufficient_storage,
|
||||
loop_detected,
|
||||
not_extended,
|
||||
network_authentication_required,
|
||||
};
|
||||
|
||||
pub fn StatusCodeType(comptime code: []const u8, comptime message: []const u8) type {
|
||||
@ -35,8 +94,67 @@ pub fn StatusCodeType(comptime code: []const u8, comptime message: []const u8) t
|
||||
}
|
||||
|
||||
pub const TaggedStatusCode = union(StatusCode) {
|
||||
ok: StatusCodeType("200", "OK"),
|
||||
@"continue": StatusCodeType("100", "Continue"),
|
||||
switching_protocols: StatusCodeType("101", "Switching Protocols"),
|
||||
processing: StatusCodeType("102", "Processing"),
|
||||
early_hints: StatusCodeType("103", "Early Hints"),
|
||||
ok: StatusCodeType("200", "Ok"),
|
||||
created: StatusCodeType("201", "Created"),
|
||||
accepted: StatusCodeType("202", "Accepted"),
|
||||
non_authoritative_info: StatusCodeType("203", "Non Authoritative Information"),
|
||||
no_content: StatusCodeType("204", "No Content"),
|
||||
reset_content: StatusCodeType("205", "Reset Content"),
|
||||
partial_content: StatusCodeType("206", "Partial Content"),
|
||||
multi_status: StatusCodeType("207", "Multi Status"),
|
||||
already_reported: StatusCodeType("208", "Already Reported"),
|
||||
im_used: StatusCodeType("226", "IM Used"),
|
||||
multiple_choice: StatusCodeType("300", "Multiple Choices"),
|
||||
moved_permanently: StatusCodeType("301", "Moved Permanently"),
|
||||
found: StatusCodeType("302", "Found"),
|
||||
see_other: StatusCodeType("303", "See Other"),
|
||||
not_modified: StatusCodeType("304", "Not Modified"),
|
||||
use_proxy: StatusCodeType("305", "Use Proxy"),
|
||||
temporary_redirect: StatusCodeType("307", "Temporary Redirect"),
|
||||
permanent_redirect: StatusCodeType("308", "Permanent Redirect"),
|
||||
bad_request: StatusCodeType("400", "Bad Request"),
|
||||
unauthorized: StatusCodeType("401", "Unauthorized"),
|
||||
payment_required: StatusCodeType("402", "Payment Required"),
|
||||
forbidden: StatusCodeType("403", "Forbidden"),
|
||||
not_found: StatusCodeType("404", "Not Found"),
|
||||
method_not_allowed: StatusCodeType("405", "Method Not Allowed"),
|
||||
not_acceptable: StatusCodeType("406", "Not Acceptable"),
|
||||
proxy_auth_required: StatusCodeType("407", "Proxy Authentication Required"),
|
||||
request_timeout: StatusCodeType("408", "Request Timeout"),
|
||||
conflict: StatusCodeType("409", "Conflict"),
|
||||
gone: StatusCodeType("410", "Gone"),
|
||||
length_required: StatusCodeType("411", "Length Required"),
|
||||
precondition_failed: StatusCodeType("412", "Precondition Failed"),
|
||||
payload_too_large: StatusCodeType("413", "Payload Too Large"),
|
||||
uri_too_long: StatusCodeType("414", "Request Uri Too Long"),
|
||||
unsupported_media_type: StatusCodeType("415", "Unsupported Media Type"),
|
||||
range_not_satisfiable: StatusCodeType("416", "Requested Range Not Satisfiable"),
|
||||
expectation_failed: StatusCodeType("417", "Expectation Failed"),
|
||||
misdirected_request: StatusCodeType("421", "Misdirected Request"),
|
||||
unprocessable_entity: StatusCodeType("422", "Unprocessable Entity"),
|
||||
locked: StatusCodeType("423", "Locked"),
|
||||
failed_dependency: StatusCodeType("424", "Failed Dependency"),
|
||||
too_early: StatusCodeType("425", "Too Early"),
|
||||
upgrade_required: StatusCodeType("426", "Upgrade Required"),
|
||||
precondition_required: StatusCodeType("428", "Precondition Required"),
|
||||
too_many_requests: StatusCodeType("429", "Too Many Requests"),
|
||||
request_header_fields_too_large: StatusCodeType("431", "Request Header Fields Too Large"),
|
||||
unavailable_for_legal_reasons: StatusCodeType("451", "Unavailable for Legal Reasons"),
|
||||
internal_server_error: StatusCodeType("500", "Internal Server Error"),
|
||||
not_implemented: StatusCodeType("501", "Not Implemented"),
|
||||
bad_gateway: StatusCodeType("502", "Bad Gateway"),
|
||||
service_unavailable: StatusCodeType("503", "Service Unavailable"),
|
||||
gateway_timeout: StatusCodeType("504", "Gateway Timeout"),
|
||||
http_version_not_supported: StatusCodeType("505", "Http Version Not Supported"),
|
||||
variant_also_negotiates: StatusCodeType("506", "Variant Also Negotiates"),
|
||||
insufficient_storage: StatusCodeType("507", "Insufficient Storage"),
|
||||
loop_detected: StatusCodeType("508", "Loop Detected"),
|
||||
not_extended: StatusCodeType("510", "Not Extended"),
|
||||
network_authentication_required: StatusCodeType("511", "Network Authentication Required"),
|
||||
|
||||
const Self = @This();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user