using Astralis; using Invercargill; using Invercargill.DataStructures; /** * ErrorHandling Example * * Demonstrates error handling and status codes in Astralis. * Uses Invercargill data structures for error logging and management. */ // Root handler class RootEndpoint : Object, Endpoint { public string route { get { return "/"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error { return new HttpStringResult("""Error Handling Demo This example demonstrates various error handling patterns in Astralis: Endpoints: GET /ok - 200 OK response GET /not-found - 404 Not Found GET /bad-request - 400 Bad Request GET /internal-error - 500 Internal Server Error GET /validation - Input validation example GET /resource/{id} - Resource lookup with error handling GET /api/{endpoint} - API endpoint simulation GET /custom-error - Custom error response GET /timeout - Simulated timeout GET /unauthorized - 401 Unauthorized GET /forbidden - 403 Forbidden GET /method-not-allowed - 405 Method Not Allowed """); } } // 200 OK response class OkEndpoint : Object, Endpoint { public string route { get { return "/ok"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error { return new HttpStringResult(@"{ \"status\": \"success\", \"message\": \"Request completed successfully\" }") .set_header("Content-Type", "application/json"); } } // 404 Not Found class NotFoundEndpoint : Object, Endpoint { public string route { get { return "/not-found"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error { return new HttpStringResult(@"{ \"error\": \"Not Found\", \"message\": \"The requested resource was not found\" }") .set_header("Content-Type", "application/json"); } } // 400 Bad Request class BadRequestEndpoint : Object, Endpoint { public string route { get { return "/bad-request"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error { return new HttpStringResult(@"{ \"error\": \"Bad Request\", \"message\": \"The request could not be understood\" }") .set_header("Content-Type", "application/json"); } } // 500 Internal Server Error class InternalErrorEndpoint : Object, Endpoint { public string route { get { return "/internal-error"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error { return new HttpStringResult(@"{ \"error\": \"Internal Server Error\", \"message\": \"An unexpected error occurred\" }") .set_header("Content-Type", "application/json"); } } // Input validation example class ValidationEndpoint : Object, Endpoint { public string route { get { return "/validation"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error { var email = http_context.request.get_query("email"); var age_str = http_context.request.get_query("age"); var error_list = new List(); if (email == null || !is_valid_email(email)) { error_list.append("Invalid email address"); } if (age_str == null) { error_list.append("Age parameter is required"); } else { var age = int.parse(age_str); if (age < 0 || age > 150) { error_list.append("Age must be between 0 and 150"); } } if (error_list.length() > 0) { var json_parts = new StringBuilder(); json_parts.append("{ \"error\": \"Validation Failed\", \"errors\": ["); bool first = true; error_list.foreach((err) => { if (!first) { json_parts.append(", "); } json_parts.append("\""); json_parts.append(err); json_parts.append("\""); first = false; }); json_parts.append("] }"); var json_string = json_parts.str; return new HttpStringResult(json_string) .set_header("Content-Type", "application/json"); } return new HttpStringResult(@"{ \"status\": \"success\", \"email\": \"$email\", \"age\": $age_str }") .set_header("Content-Type", "application/json"); } } // Resource lookup with error handling class ResourceEndpoint : Object, Endpoint { public string route { get { return "/resource/{id}"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error { var id_str = route_info.named_components["id"]; int id; if (!int.try_parse(id_str, out id)) { return error_response(@"Invalid resource ID: $id_str", StatusCode.BAD_REQUEST); } // Simulate resource lookup using array var resource_array = new Resource[3]; resource_array[0] = new Resource(1, "Resource One", "Description of resource one"); resource_array[1] = new Resource(2, "Resource Two", "Description of resource two"); resource_array[2] = new Resource(3, "Resource Three", "Description of resource three"); Resource? resource = null; foreach (var r in resource_array) { if (r.id == id) { resource = r; break; } } if (resource == null) { return error_response(@"Resource with ID $id not found", StatusCode.NOT_FOUND); } return new HttpStringResult(resource.to_json()) .set_header("Content-Type", "application/json"); } } // API endpoint simulation class ApiEndpoint : Object, Endpoint { public string route { get { return "/api/{endpoint}"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route_info) throws Error { var endpoint = route_info.named_components["endpoint"]; // Simulate different API responses switch (endpoint) { case "users": return new HttpStringResult(@"{ \"users\": [{\"id\": 1, \"name\": \"Alice\"}, {\"id\": 2, \"name\": \"Bob\"}] }") .set_header("Content-Type", "application/json"); case "posts": return new HttpStringResult(@"{ \"posts\": [{\"id\": 1, \"title\": \"First Post\"}, {\"id\": 2, \"title\": \"Second Post\"}] }") .set_header("Content-Type", "application/json"); case "invalid": return error_response("Invalid API endpoint", StatusCode.NOT_FOUND); default: return error_response(@"Unknown endpoint: $endpoint", StatusCode.NOT_FOUND); } } } // Custom error response class CustomErrorEndpoint : Object, Endpoint { public string route { get { return "/custom-error"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error { var error_code = http_context.request.get_query_or_default("code", "CUSTOM_ERROR"); var message = http_context.request.get_query_or_default("message", "A custom error occurred"); var timestamp = new DateTime.now_local().format_iso8601(); var request_id = generate_request_id(); var json = @"{ \"error\": \"$error_code\", \"message\": \"$message\", \"timestamp\": \"$timestamp\", \"request_id\": \"$request_id\" }"; return new HttpStringResult(json) .set_header("Content-Type", "application/json"); } } // Simulated timeout (actually returns 408) class TimeoutEndpoint : Object, Endpoint { public string route { get { return "/timeout"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error { return new HttpStringResult(@"{ \"error\": \"Request Timeout\", \"message\": \"The request took too long to complete\" }") .set_header("Content-Type", "application/json"); } } // 401 Unauthorized class UnauthorizedEndpoint : Object, Endpoint { public string route { get { return "/unauthorized"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error { var auth_header = http_context.request.get_header("Authorization"); if (auth_header == null) { return new HttpStringResult(@"{ \"error\": \"Unauthorized\", \"message\": \"Authentication required\" }") .set_header("Content-Type", "application/json") .set_header("WWW-Authenticate", "Bearer realm=\"api\""); } // Check auth token (simplified) if (!auth_header.contains("valid-token")) { return new HttpStringResult(@"{ \"error\": \"Unauthorized\", \"message\": \"Invalid authentication token\" }") .set_header("Content-Type", "application/json") .set_header("WWW-Authenticate", "Bearer error=\"invalid_token\""); } return new HttpStringResult(@"{ \"status\": \"success\", \"message\": \"Authenticated\" }") .set_header("Content-Type", "application/json"); } } // 403 Forbidden class ForbiddenEndpoint : Object, Endpoint { public string route { get { return "/forbidden"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error { var user_role = http_context.request.get_cookie("role") ?? "guest"; if (user_role != "admin") { return new HttpStringResult(@"{ \"error\": \"Forbidden\", \"message\": \"You don't have permission to access this resource\" }") .set_header("Content-Type", "application/json"); } return new HttpStringResult(@"{ \"status\": \"success\", \"message\": \"Admin access granted\" }") .set_header("Content-Type", "application/json"); } } // 405 Method Not Allowed class MethodNotAllowedEndpoint : Object, Endpoint { public string route { get { return "/method-not-allowed"; } } public Method[] methods { owned get { return { Method.GET }; } } public async HttpResult handle_request(HttpContext http_context, RouteInformation route) throws Error { return new HttpStringResult(@"{ \"error\": \"Method Not Allowed\", \"message\": \"GET method is not allowed for this endpoint\" }") .set_header("Content-Type", "application/json") .set_header("Allow", "POST, PUT, DELETE"); } } void main() { var router = new EndpointRouter() .add_endpoint(new RootEndpoint()) .add_endpoint(new OkEndpoint()) .add_endpoint(new NotFoundEndpoint()) .add_endpoint(new BadRequestEndpoint()) .add_endpoint(new InternalErrorEndpoint()) .add_endpoint(new ValidationEndpoint()) .add_endpoint(new ResourceEndpoint()) .add_endpoint(new ApiEndpoint()) .add_endpoint(new CustomErrorEndpoint()) .add_endpoint(new TimeoutEndpoint()) .add_endpoint(new UnauthorizedEndpoint()) .add_endpoint(new ForbiddenEndpoint()) .add_endpoint(new MethodNotAllowedEndpoint()); var pipeline = new Pipeline() .add_component(router); var server = new Server(8088, pipeline); print("Error Handling Demo Server running on port 8088\n"); print("Try these endpoints:\n"); print(" - http://localhost:8088/\n"); print(" - http://localhost:8088/ok\n"); print(" - http://localhost:8088/not-found\n"); print(" - http://localhost:8088/bad-request\n"); print(" - http://localhost:8088/internal-error\n"); print(" - http://localhost:8088/validation?email=test&age=25\n"); print(" - http://localhost:8088/resource/1\n"); print(" - http://localhost:8088/resource/999\n"); print(" - http://localhost:8088/api/users\n"); print(" - http://localhost:8088/custom-error?code=TEST&message=Test+error\n"); print(" - http://localhost:8088/timeout\n"); print(" - http://localhost:8088/unauthorized\n"); print(" - http://localhost:8088/forbidden\n"); print(" - http://localhost:8088/method-not-allowed\n"); server.run(); } // Helper functions HttpResult error_response(string message, StatusCode status) { var json = @"{ \"error\": \"Error\", \"message\": \"$message\" }"; return new HttpStringResult(json) .set_header("Content-Type", "application/json"); } string generate_request_id() { var timestamp = new DateTime.now_local().to_unix(); var random = Random.next_int(); return @"req-$timestamp-$random"; } bool is_valid_email(string email) { // Simple email validation return email.contains("@") && email.contains(".") && email.length > 5; } // Helper classes class Resource { public int id { get; private set; } public string name { get; private set; } public string description { get; private set; } public Resource(int id, string name, string description) { this.id = id; this.name = name; this.description = description; } public string to_json() { return @"{ \"id\": $id, \"name\": \"$name\", \"description\": \"$description\" }"; } }