|
|
@@ -1,562 +0,0 @@
|
|
|
-using Invercargill;
|
|
|
-using Invercargill.DataStructures;
|
|
|
-
|
|
|
-namespace Astralis {
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// HTTP router that maps request paths to handlers with support for named segments and wildcards.
|
|
|
- /// </summary>
|
|
|
- /// <example>
|
|
|
- /// var router = new Router();
|
|
|
- /// router.map("/users", users_handler);
|
|
|
- /// router.map("/users/{id}", user_detail_handler);
|
|
|
- /// router.map("/files/{filename}/details", file_details_handler);
|
|
|
- /// router.map("/files/*.jpg", jpg_handler); // Single wildcard with suffix
|
|
|
- /// router.map("/files/*", file_handler); // Single wildcard (one segment)
|
|
|
- /// router.map("/downloads/**", download_handler); // Greedy wildcard (any depth)
|
|
|
- /// router.map("/images/**.png", png_handler); // Greedy wildcard with suffix
|
|
|
- /// router.not_found_handler = new NotFoundHandler();
|
|
|
- /// router.error_handler = new ErrorHandler();
|
|
|
- /// </example>
|
|
|
- public class Router : RequestHandler, Object {
|
|
|
-
|
|
|
- private Vector<Route> _routes = new Vector<Route>();
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Handler invoked when no route matches the request path.
|
|
|
- /// </summary>
|
|
|
- public RequestHandler not_found_handler { get; set; }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Handler invoked when a route handler throws an error.
|
|
|
- /// </summary>
|
|
|
- public RouteErrorHandler error_handler { get; set; }
|
|
|
-
|
|
|
- public Router() {
|
|
|
- not_found_handler = new DefaultNotFoundHandler();
|
|
|
- error_handler = new DefaultErrorHandler();
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Registers a route handler for the specified path pattern.
|
|
|
- ///
|
|
|
- /// Pattern syntax:
|
|
|
- /// - Literal segments: "/users" matches exactly "/users"
|
|
|
- /// - Named segments: "/users/{id}" captures the segment value as "id"
|
|
|
- /// - Single wildcard (*): "/files/*" matches exactly one segment (e.g., "/files/test")
|
|
|
- /// - Single wildcard with suffix (*.ext): "/files/*.jpg" matches one segment ending in .jpg
|
|
|
- /// - Greedy wildcard (**): "/files/**" matches zero or more segments (e.g., "/files/a/b/c")
|
|
|
- /// - Greedy wildcard with suffix (**.ext): "/files/**.jpg" matches paths ending in .jpg
|
|
|
- /// </summary>
|
|
|
- /// <param name="pattern">The path pattern to match</param>
|
|
|
- /// <param name="handler">The handler to invoke when the pattern matches</param>
|
|
|
- /// <param name="methods">Optional HTTP methods to restrict this route to (e.g., "GET", "POST")</param>
|
|
|
- public void map(string pattern, owned RouteHandler handler, string[]? methods = null) {
|
|
|
- var route = new Route(pattern, handler, methods);
|
|
|
- _routes.add(route);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Registers a route handler for GET requests.
|
|
|
- /// </summary>
|
|
|
- public new void get(string pattern, owned RouteHandler handler) {
|
|
|
- map(pattern, handler, { "GET" });
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Registers a route handler for POST requests.
|
|
|
- /// </summary>
|
|
|
- public void post(string pattern, owned RouteHandler handler) {
|
|
|
- map(pattern, handler, { "POST" });
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Registers a route handler for PUT requests.
|
|
|
- /// </summary>
|
|
|
- public void put(string pattern, owned RouteHandler handler) {
|
|
|
- map(pattern, handler, { "PUT" });
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Registers a route handler for DELETE requests.
|
|
|
- /// </summary>
|
|
|
- public void delete(string pattern, owned RouteHandler handler) {
|
|
|
- map(pattern, handler, { "DELETE" });
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Registers a route handler for PATCH requests.
|
|
|
- /// </summary>
|
|
|
- public void patch(string pattern, owned RouteHandler handler) {
|
|
|
- map(pattern, handler, { "PATCH" });
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Removes all registered routes.
|
|
|
- /// </summary>
|
|
|
- public void clear() {
|
|
|
- _routes.clear();
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Handles an incoming HTTP request by matching against registered routes.
|
|
|
- /// </summary>
|
|
|
- public async HttpResult handle_request(HttpContext context) throws Error {
|
|
|
- var request_path = context.request.raw_path.split("?")[0];
|
|
|
-
|
|
|
- var routes_array = _routes.to_array();
|
|
|
- foreach (var route in routes_array) {
|
|
|
- var match_result = route.match(request_path, context.request.method);
|
|
|
- if (match_result != null) {
|
|
|
- try {
|
|
|
- return yield route.handler.handle_route(context, match_result);
|
|
|
- } catch (Error e) {
|
|
|
- if (error_handler != null) {
|
|
|
- return yield error_handler.handle_route_error(context, match_result, e);
|
|
|
- }
|
|
|
- throw e;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // No route matched - use not found handler
|
|
|
- return yield not_found_handler.handle_request(context);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Represents the type of a route segment.
|
|
|
- /// </summary>
|
|
|
- internal enum SegmentType {
|
|
|
- /// <summary>Exact literal match required (e.g., "users")</summary>
|
|
|
- LITERAL,
|
|
|
- /// <summary>Named segment that captures its value (e.g., "{id}")</summary>
|
|
|
- NAMED,
|
|
|
- /// <summary>Single wildcard matching exactly one segment (e.g., "*")</summary>
|
|
|
- SINGLE_WILDCARD,
|
|
|
- /// <summary>Single wildcard with suffix requirement (e.g., "*.jpg")</summary>
|
|
|
- SINGLE_WILDCARD_SUFFIX,
|
|
|
- /// <summary>Greedy wildcard matching zero or more segments (e.g., "**")</summary>
|
|
|
- GREEDY_WILDCARD,
|
|
|
- /// <summary>Greedy wildcard with suffix requirement (e.g., "**.jpg")</summary>
|
|
|
- GREEDY_WILDCARD_SUFFIX
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Represents a parsed route segment with its type and metadata.
|
|
|
- /// </summary>
|
|
|
- internal class ParsedSegment : Object {
|
|
|
- public SegmentType segment_type;
|
|
|
- public string value;
|
|
|
- public string suffix;
|
|
|
-
|
|
|
- public ParsedSegment(SegmentType type, string value, string suffix = "") {
|
|
|
- this.segment_type = type;
|
|
|
- this.value = value;
|
|
|
- this.suffix = suffix;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Represents a single route with its pattern, handler, and allowed methods.
|
|
|
- /// Supports wildcards: "*" for single segment, "**" for greedy multi-segment matching.
|
|
|
- /// </summary>
|
|
|
- internal class Route : Object {
|
|
|
-
|
|
|
- private string _pattern;
|
|
|
- private ParsedSegment[] _parsed_segments;
|
|
|
- public RouteHandler handler { get; private set; }
|
|
|
- private string[]? _allowed_methods;
|
|
|
-
|
|
|
- /// <summary>True if this route contains a greedy wildcard (**)</summary>
|
|
|
- private bool _has_greedy_wildcard;
|
|
|
- /// <summary>The suffix for greedy wildcard, if any (e.g., ".jpg" for "**.jpg")</summary>
|
|
|
- private string _greedy_suffix = "";
|
|
|
-
|
|
|
- public Route(string pattern, owned RouteHandler handler, string[]? methods = null) {
|
|
|
- _pattern = pattern;
|
|
|
- _handler = handler;
|
|
|
- _allowed_methods = methods != null && methods.length > 0 ? methods : null;
|
|
|
- _has_greedy_wildcard = false;
|
|
|
-
|
|
|
- // Parse pattern into segments
|
|
|
- var segments = pattern.split("/");
|
|
|
- _parsed_segments = new ParsedSegment[0];
|
|
|
-
|
|
|
- foreach (var segment in segments) {
|
|
|
- if (segment != "") {
|
|
|
- var parsed = parse_segment(segment);
|
|
|
- _parsed_segments += parsed;
|
|
|
-
|
|
|
- if (parsed.segment_type == SegmentType.GREEDY_WILDCARD ||
|
|
|
- parsed.segment_type == SegmentType.GREEDY_WILDCARD_SUFFIX) {
|
|
|
- _has_greedy_wildcard = true;
|
|
|
- _greedy_suffix = parsed.suffix;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Parses a single segment into its type and components.
|
|
|
- /// </summary>
|
|
|
- private ParsedSegment parse_segment(string segment) {
|
|
|
- // Check for named segment {name}
|
|
|
- if (segment.length >= 3 && segment[0] == '{' && segment[segment.length - 1] == '}') {
|
|
|
- return new ParsedSegment(SegmentType.NAMED, segment.substring(1, segment.length - 2));
|
|
|
- }
|
|
|
-
|
|
|
- // Check for greedy wildcard ** or **.suffix
|
|
|
- if (segment.has_prefix("**")) {
|
|
|
- string suffix = segment.substring(2);
|
|
|
- if (suffix.length > 0) {
|
|
|
- return new ParsedSegment(SegmentType.GREEDY_WILDCARD_SUFFIX, "**", suffix);
|
|
|
- }
|
|
|
- return new ParsedSegment(SegmentType.GREEDY_WILDCARD, "**");
|
|
|
- }
|
|
|
-
|
|
|
- // Check for single wildcard * or *.suffix
|
|
|
- if (segment.has_prefix("*")) {
|
|
|
- string suffix = segment.substring(1);
|
|
|
- if (suffix.length > 0) {
|
|
|
- return new ParsedSegment(SegmentType.SINGLE_WILDCARD_SUFFIX, "*", suffix);
|
|
|
- }
|
|
|
- return new ParsedSegment(SegmentType.SINGLE_WILDCARD, "*");
|
|
|
- }
|
|
|
-
|
|
|
- // Literal segment
|
|
|
- return new ParsedSegment(SegmentType.LITERAL, segment);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Attempts to match the given path and method against this route.
|
|
|
- /// </summary>
|
|
|
- /// <returns>A RouteContext if matched, null otherwise</returns>
|
|
|
- public RouteContext? match(string path, string method) {
|
|
|
- // Check method first
|
|
|
- if (_allowed_methods != null) {
|
|
|
- bool method_allowed = false;
|
|
|
- foreach (var allowed in _allowed_methods) {
|
|
|
- if (allowed == method) {
|
|
|
- method_allowed = true;
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- if (!method_allowed) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Parse path into segments
|
|
|
- var path_segments = path.split("/");
|
|
|
- var path_segs = new string[0];
|
|
|
- foreach (var segment in path_segments) {
|
|
|
- if (segment != "") {
|
|
|
- path_segs += Uri.unescape_string(segment) ?? segment;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Use different matching strategy based on whether we have greedy wildcards
|
|
|
- if (_has_greedy_wildcard) {
|
|
|
- return match_with_greedy(path_segs);
|
|
|
- } else {
|
|
|
- return match_simple(path_segs);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Simple matching for routes without greedy wildcards.
|
|
|
- /// Requires exact segment count match.
|
|
|
- /// </summary>
|
|
|
- private RouteContext? match_simple(string[] path_segs) {
|
|
|
- // Check segment count - must match exactly
|
|
|
- if (path_segs.length != _parsed_segments.length) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- // Match each segment
|
|
|
- var named_segments = new Dictionary<string, string>();
|
|
|
- var wildcard_captures = new Vector<string>();
|
|
|
-
|
|
|
- for (int i = 0; i < _parsed_segments.length; i++) {
|
|
|
- var parsed = _parsed_segments[i];
|
|
|
- var path_seg = path_segs[i];
|
|
|
-
|
|
|
- if (!match_segment(parsed, path_seg, named_segments, wildcard_captures)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Build route context
|
|
|
- return build_context(path_segs, named_segments, wildcard_captures);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Matching for routes with greedy wildcards (**).
|
|
|
- /// Allows variable segment count.
|
|
|
- /// </summary>
|
|
|
- private RouteContext? match_with_greedy(string[] path_segs) {
|
|
|
- var named_segments = new Dictionary<string, string>();
|
|
|
- var wildcard_captures = new Vector<string>();
|
|
|
-
|
|
|
- int path_index = 0;
|
|
|
- int pattern_index = 0;
|
|
|
-
|
|
|
- while (pattern_index < _parsed_segments.length && path_index <= path_segs.length) {
|
|
|
- var parsed = _parsed_segments[pattern_index];
|
|
|
-
|
|
|
- if (parsed.segment_type == SegmentType.GREEDY_WILDCARD ||
|
|
|
- parsed.segment_type == SegmentType.GREEDY_WILDCARD_SUFFIX) {
|
|
|
- // Find how many segments this greedy wildcard should consume
|
|
|
- int remaining_patterns = _parsed_segments.length - pattern_index - 1;
|
|
|
- int min_path_needed = remaining_patterns; // Minimum segments needed for remaining patterns
|
|
|
-
|
|
|
- // Check if we have enough path segments
|
|
|
- if (path_segs.length - path_index < min_path_needed) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- // Calculate how many segments the greedy wildcard consumes
|
|
|
- int greedy_count = path_segs.length - path_index - min_path_needed;
|
|
|
-
|
|
|
- // For greedy with suffix, the last consumed segment must match the suffix
|
|
|
- if (parsed.segment_type == SegmentType.GREEDY_WILDCARD_SUFFIX) {
|
|
|
- if (greedy_count == 0) {
|
|
|
- // Need at least one segment to match the suffix
|
|
|
- return null;
|
|
|
- }
|
|
|
- int last_greedy_index = path_index + greedy_count - 1;
|
|
|
- if (last_greedy_index >= path_segs.length) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- string last_seg = path_segs[last_greedy_index];
|
|
|
- if (!last_seg.has_suffix(parsed.suffix)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Capture all segments consumed by greedy wildcard
|
|
|
- var greedy_segments = new string[0];
|
|
|
- for (int i = 0; i < greedy_count; i++) {
|
|
|
- string seg = path_segs[path_index + i];
|
|
|
- greedy_segments += seg;
|
|
|
- wildcard_captures.add(seg);
|
|
|
- }
|
|
|
-
|
|
|
- // Store the greedy capture as a single string with "/" separator
|
|
|
- if (greedy_segments.length > 0) {
|
|
|
- named_segments["*"] = string.joinv("/", greedy_segments);
|
|
|
- } else {
|
|
|
- named_segments["*"] = "";
|
|
|
- }
|
|
|
-
|
|
|
- path_index += greedy_count;
|
|
|
- pattern_index++;
|
|
|
- } else {
|
|
|
- // Non-greedy segment - match normally
|
|
|
- if (path_index >= path_segs.length) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- if (!match_segment(parsed, path_segs[path_index], named_segments, wildcard_captures)) {
|
|
|
- return null;
|
|
|
- }
|
|
|
- path_index++;
|
|
|
- pattern_index++;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Check if we've consumed all path segments and all patterns
|
|
|
- if (path_index != path_segs.length || pattern_index != _parsed_segments.length) {
|
|
|
- return null;
|
|
|
- }
|
|
|
-
|
|
|
- return build_context(path_segs, named_segments, wildcard_captures);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Matches a single path segment against a parsed pattern segment.
|
|
|
- /// </summary>
|
|
|
- private bool match_segment(ParsedSegment parsed, string path_seg,
|
|
|
- Dictionary<string, string> named_segments,
|
|
|
- Vector<string> wildcard_captures) {
|
|
|
- switch (parsed.segment_type) {
|
|
|
- case SegmentType.LITERAL:
|
|
|
- return path_seg == parsed.value;
|
|
|
-
|
|
|
- case SegmentType.NAMED:
|
|
|
- named_segments[parsed.value] = path_seg;
|
|
|
- return true;
|
|
|
-
|
|
|
- case SegmentType.SINGLE_WILDCARD:
|
|
|
- // Matches any non-empty segment
|
|
|
- wildcard_captures.add(path_seg);
|
|
|
- return true;
|
|
|
-
|
|
|
- case SegmentType.SINGLE_WILDCARD_SUFFIX:
|
|
|
- // Must end with the suffix
|
|
|
- if (path_seg.has_suffix(parsed.suffix)) {
|
|
|
- wildcard_captures.add(path_seg);
|
|
|
- return true;
|
|
|
- }
|
|
|
- return false;
|
|
|
-
|
|
|
- default:
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Builds a RouteContext from the match results.
|
|
|
- /// </summary>
|
|
|
- private RouteContext build_context(string[] path_segs,
|
|
|
- Dictionary<string, string> named_segments,
|
|
|
- Vector<string> wildcard_captures) {
|
|
|
- var context = new RouteContext();
|
|
|
- context.segments = Wrap.array<string>(path_segs).to_series();
|
|
|
- context.named_segments = named_segments;
|
|
|
- context.wildcard_captures = wildcard_captures.to_series();
|
|
|
- return context;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Interface for route handlers that process matched routes.
|
|
|
- /// </summary>
|
|
|
- public interface RouteHandler : Object {
|
|
|
- /// <summary>
|
|
|
- /// Handles a matched route.
|
|
|
- /// </summary>
|
|
|
- /// <param name="http_context">The HTTP context containing request information</param>
|
|
|
- /// <param name="route_context">The route context containing matched segments</param>
|
|
|
- /// <returns>The HTTP result to send back to the client</returns>
|
|
|
- public abstract async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Interface for error handlers that process exceptions from route handlers.
|
|
|
- /// </summary>
|
|
|
- public interface RouteErrorHandler : Object {
|
|
|
- /// <summary>
|
|
|
- /// Handles an error that occurred during route processing.
|
|
|
- /// </summary>
|
|
|
- /// <param name="http_context">The HTTP context containing request information</param>
|
|
|
- /// <param name="route_context">The route context for the matched route</param>
|
|
|
- /// <param name="error">The error that was thrown</param>
|
|
|
- /// <returns>The HTTP result to send back to the client</returns>
|
|
|
- public abstract async HttpResult handle_route_error(HttpContext http_context, RouteContext route_context, Error error) throws Error;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Context information for a matched route, containing path segments, named captures, and wildcard captures.
|
|
|
- /// </summary>
|
|
|
- public class RouteContext : Object {
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// The path segments from the matched URL.
|
|
|
- /// </summary>
|
|
|
- public Series<string> segments { get; set; }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Named segment values captured from the route pattern.
|
|
|
- /// For pattern "/users/{id}/posts/{post_id}" matching "/users/123/posts/456",
|
|
|
- /// this would contain {"id": "123", "post_id": "456"}.
|
|
|
- ///
|
|
|
- /// For greedy wildcards (**), the key "*" contains the matched path segments
|
|
|
- /// joined with "/" (e.g., for "/files/**" matching "/files/a/b/c", this would
|
|
|
- /// contain {"*": "a/b/c"}).
|
|
|
- /// </summary>
|
|
|
- public Dictionary<string, string> named_segments { get; set; }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// All values captured by wildcard segments (* or **).
|
|
|
- /// Each wildcard match adds its captured value(s) to this series.
|
|
|
- ///
|
|
|
- /// For single wildcards (*), each matching segment is added individually.
|
|
|
- /// For greedy wildcards (**), all consumed segments are added individually.
|
|
|
- ///
|
|
|
- /// Example: Pattern "/files/*/*.jpg" matching "/files/images/photo.jpg"
|
|
|
- /// would have wildcard_captures = ["images", "photo.jpg"]
|
|
|
- /// </summary>
|
|
|
- public Series<string> wildcard_captures { get; set; }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets a named segment value by name.
|
|
|
- /// </summary>
|
|
|
- /// <param name="name">The name of the segment to retrieve</param>
|
|
|
- /// <returns>The segment value, or null if not found</returns>
|
|
|
- public string? get_segment(string name) {
|
|
|
- string? value = null;
|
|
|
- try {
|
|
|
- value = named_segments.get(name);
|
|
|
- } catch (Error e) {
|
|
|
- // Key not found
|
|
|
- }
|
|
|
- return value;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets a named segment value by name, with a default fallback.
|
|
|
- /// </summary>
|
|
|
- /// <param name="name">The name of the segment to retrieve</param>
|
|
|
- /// <param name="default_value">The default value if not found</param>
|
|
|
- /// <returns>The segment value, or the default if not found</returns>
|
|
|
- public string get_segment_or_default(string name, string default_value) {
|
|
|
- string? value = null;
|
|
|
- try {
|
|
|
- value = named_segments.get(name);
|
|
|
- } catch (Error e) {
|
|
|
- // Key not found
|
|
|
- }
|
|
|
- return value ?? default_value;
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Checks if a named segment exists.
|
|
|
- /// </summary>
|
|
|
- /// <param name="name">The name of the segment to check</param>
|
|
|
- /// <returns>True if the segment exists, false otherwise</returns>
|
|
|
- public bool has_segment(string name) {
|
|
|
- return named_segments.has(name);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Gets the greedy wildcard capture value (from ** patterns).
|
|
|
- /// This is a convenience method for getting the "*" key from named_segments.
|
|
|
- /// </summary>
|
|
|
- /// <returns>The greedy wildcard capture as a path string, or null if not present</returns>
|
|
|
- public string? get_greedy_capture() {
|
|
|
- return get_segment("*");
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// A simple not found handler that returns a 404 response.
|
|
|
- /// </summary>
|
|
|
- public class DefaultNotFoundHandler : RequestHandler, Object {
|
|
|
-
|
|
|
- public async HttpResult handle_request(HttpContext http_context) throws Error {
|
|
|
- return new HttpStringResult("Not Found", StatusCode.NOT_FOUND);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// A simple error handler that returns a 500 response with error details.
|
|
|
- /// </summary>
|
|
|
- public class DefaultErrorHandler : RouteErrorHandler, Object {
|
|
|
-
|
|
|
- public bool include_details { get; set; }
|
|
|
-
|
|
|
- public DefaultErrorHandler(bool include_details = false) {
|
|
|
- this.include_details = include_details;
|
|
|
- }
|
|
|
-
|
|
|
- public async HttpResult handle_route_error(HttpContext http_context, RouteContext route_context, Error error) throws Error {
|
|
|
- var message = include_details
|
|
|
- ? @"Internal Server Error: $(error.message)"
|
|
|
- : "Internal Server Error";
|
|
|
-
|
|
|
- return new HttpStringResult(message, StatusCode.INTERNAL_SERVER_ERROR);
|
|
|
- }
|
|
|
- }
|
|
|
-}
|