ErrorHandling.vala 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. using Astralis;
  2. using Invercargill;
  3. using Invercargill.DataStructures;
  4. /**
  5. * ErrorHandling Example
  6. *
  7. * Demonstrates error handling and status codes in Astralis.
  8. * Uses Invercargill data structures for error logging and management.
  9. */
  10. // Root handler
  11. class RootEndpoint : Object, Endpoint {
  12. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  13. return new HttpStringResult("""Error Handling Demo
  14. This example demonstrates various error handling patterns in Astralis:
  15. Endpoints:
  16. GET /ok - 200 OK response
  17. GET /not-found - 404 Not Found
  18. GET /bad-request - 400 Bad Request
  19. GET /internal-error - 500 Internal Server Error
  20. GET /validation - Input validation example
  21. GET /resource/{id} - Resource lookup with error handling
  22. GET /api/{endpoint} - API endpoint simulation
  23. GET /custom-error - Custom error response
  24. GET /timeout - Simulated timeout
  25. GET /unauthorized - 401 Unauthorized
  26. GET /forbidden - 403 Forbidden
  27. GET /method-not-allowed - 405 Method Not Allowed
  28. """);
  29. }
  30. }
  31. // 200 OK response
  32. class OkEndpoint : Object, Endpoint {
  33. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  34. return new HttpStringResult(@"{ \"status\": \"success\", \"message\": \"Request completed successfully\" }")
  35. .set_header("Content-Type", "application/json");
  36. }
  37. }
  38. // 404 Not Found
  39. class NotFoundEndpoint : Object, Endpoint {
  40. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  41. return new HttpStringResult(@"{ \"error\": \"Not Found\", \"message\": \"The requested resource was not found\" }")
  42. .set_header("Content-Type", "application/json");
  43. }
  44. }
  45. // 400 Bad Request
  46. class BadRequestEndpoint : Object, Endpoint {
  47. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  48. return new HttpStringResult(@"{ \"error\": \"Bad Request\", \"message\": \"The request could not be understood\" }")
  49. .set_header("Content-Type", "application/json");
  50. }
  51. }
  52. // 500 Internal Server Error
  53. class InternalErrorEndpoint : Object, Endpoint {
  54. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  55. return new HttpStringResult(@"{ \"error\": \"Internal Server Error\", \"message\": \"An unexpected error occurred\" }")
  56. .set_header("Content-Type", "application/json");
  57. }
  58. }
  59. // Input validation example
  60. class ValidationEndpoint : Object, Endpoint {
  61. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  62. var email = http_context.request.get_query("email");
  63. var age_str = http_context.request.get_query("age");
  64. var error_list = new List<string>();
  65. if (email == null || !is_valid_email(email)) {
  66. error_list.append("Invalid email address");
  67. }
  68. if (age_str == null) {
  69. error_list.append("Age parameter is required");
  70. } else {
  71. var age = int.parse(age_str);
  72. if (age < 0 || age > 150) {
  73. error_list.append("Age must be between 0 and 150");
  74. }
  75. }
  76. if (error_list.length() > 0) {
  77. var json_parts = new StringBuilder();
  78. json_parts.append("{ \"error\": \"Validation Failed\", \"errors\": [");
  79. bool first = true;
  80. error_list.foreach((err) => {
  81. if (!first) {
  82. json_parts.append(", ");
  83. }
  84. json_parts.append("\"");
  85. json_parts.append(err);
  86. json_parts.append("\"");
  87. first = false;
  88. });
  89. json_parts.append("] }");
  90. var json_string = json_parts.str;
  91. return new HttpStringResult(json_string)
  92. .set_header("Content-Type", "application/json");
  93. }
  94. return new HttpStringResult(@"{ \"status\": \"success\", \"email\": \"$email\", \"age\": $age_str }")
  95. .set_header("Content-Type", "application/json");
  96. }
  97. }
  98. // Resource lookup with error handling
  99. class ResourceEndpoint : Object, Endpoint {
  100. public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
  101. string? id_str = null;
  102. route_info.mapped_parameters.try_get("id", out id_str);
  103. int id;
  104. if (!int.try_parse(id_str, out id)) {
  105. return error_response(@"Invalid resource ID: $id_str", StatusCode.BAD_REQUEST);
  106. }
  107. // Simulate resource lookup using array
  108. var resource_array = new Resource[3];
  109. resource_array[0] = new Resource(1, "Resource One", "Description of resource one");
  110. resource_array[1] = new Resource(2, "Resource Two", "Description of resource two");
  111. resource_array[2] = new Resource(3, "Resource Three", "Description of resource three");
  112. Resource? resource = null;
  113. foreach (var r in resource_array) {
  114. if (r.id == id) {
  115. resource = r;
  116. break;
  117. }
  118. }
  119. if (resource == null) {
  120. return error_response(@"Resource with ID $id not found", StatusCode.NOT_FOUND);
  121. }
  122. return new HttpStringResult(resource.to_json())
  123. .set_header("Content-Type", "application/json");
  124. }
  125. }
  126. // API endpoint simulation
  127. class ApiEndpoint : Object, Endpoint {
  128. public async HttpResult handle_request(HttpContext http_context, RouteContext route_info) throws Error {
  129. string? endpoint = null;
  130. route_info.mapped_parameters.try_get("endpoint", out endpoint);
  131. // Simulate different API responses
  132. switch (endpoint) {
  133. case "users":
  134. return new HttpStringResult(@"{ \"users\": [{\"id\": 1, \"name\": \"Alice\"}, {\"id\": 2, \"name\": \"Bob\"}] }")
  135. .set_header("Content-Type", "application/json");
  136. case "posts":
  137. return new HttpStringResult(@"{ \"posts\": [{\"id\": 1, \"title\": \"First Post\"}, {\"id\": 2, \"title\": \"Second Post\"}] }")
  138. .set_header("Content-Type", "application/json");
  139. case "invalid":
  140. return error_response("Invalid API endpoint", StatusCode.NOT_FOUND);
  141. default:
  142. return error_response(@"Unknown endpoint: $endpoint", StatusCode.NOT_FOUND);
  143. }
  144. }
  145. }
  146. // Custom error response
  147. class CustomErrorEndpoint : Object, Endpoint {
  148. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  149. var error_code = http_context.request.get_query_or_default("code", "CUSTOM_ERROR");
  150. var message = http_context.request.get_query_or_default("message", "A custom error occurred");
  151. var timestamp = new DateTime.now_local().format_iso8601();
  152. var request_id = generate_request_id();
  153. var json = @"{
  154. \"error\": \"$error_code\",
  155. \"message\": \"$message\",
  156. \"timestamp\": \"$timestamp\",
  157. \"request_id\": \"$request_id\"
  158. }";
  159. return new HttpStringResult(json)
  160. .set_header("Content-Type", "application/json");
  161. }
  162. }
  163. // Simulated timeout (actually returns 408)
  164. class TimeoutEndpoint : Object, Endpoint {
  165. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  166. return new HttpStringResult(@"{ \"error\": \"Request Timeout\", \"message\": \"The request took too long to complete\" }")
  167. .set_header("Content-Type", "application/json");
  168. }
  169. }
  170. // 401 Unauthorized
  171. class UnauthorizedEndpoint : Object, Endpoint {
  172. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  173. var auth_header = http_context.request.get_header("Authorization");
  174. if (auth_header == null) {
  175. return new HttpStringResult(@"{ \"error\": \"Unauthorized\", \"message\": \"Authentication required\" }")
  176. .set_header("Content-Type", "application/json")
  177. .set_header("WWW-Authenticate", "Bearer realm=\"api\"");
  178. }
  179. // Check auth token (simplified)
  180. if (!auth_header.contains("valid-token")) {
  181. return new HttpStringResult(@"{ \"error\": \"Unauthorized\", \"message\": \"Invalid authentication token\" }")
  182. .set_header("Content-Type", "application/json")
  183. .set_header("WWW-Authenticate", "Bearer error=\"invalid_token\"");
  184. }
  185. return new HttpStringResult(@"{ \"status\": \"success\", \"message\": \"Authenticated\" }")
  186. .set_header("Content-Type", "application/json");
  187. }
  188. }
  189. // 403 Forbidden
  190. class ForbiddenEndpoint : Object, Endpoint {
  191. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  192. var user_role = http_context.request.get_cookie("role") ?? "guest";
  193. if (user_role != "admin") {
  194. return new HttpStringResult(@"{ \"error\": \"Forbidden\", \"message\": \"You don't have permission to access this resource\" }")
  195. .set_header("Content-Type", "application/json");
  196. }
  197. return new HttpStringResult(@"{ \"status\": \"success\", \"message\": \"Admin access granted\" }")
  198. .set_header("Content-Type", "application/json");
  199. }
  200. }
  201. // 405 Method Not Allowed
  202. class MethodNotAllowedEndpoint : Object, Endpoint {
  203. public async HttpResult handle_request(HttpContext http_context, RouteContext route) throws Error {
  204. return new HttpStringResult(@"{ \"error\": \"Method Not Allowed\", \"message\": \"GET method is not allowed for this endpoint\" }")
  205. .set_header("Content-Type", "application/json")
  206. .set_header("Allow", "POST, PUT, DELETE");
  207. }
  208. }
  209. void main() {
  210. var application = new WebApplication(8088);
  211. application.container.register_scoped<Endpoint>(() => new RootEndpoint())
  212. .with_metadata<EndpointRoute>(new EndpointRoute("/"));
  213. application.container.register_scoped<Endpoint>(() => new OkEndpoint())
  214. .with_metadata<EndpointRoute>(new EndpointRoute("/ok"));
  215. application.container.register_scoped<Endpoint>(() => new NotFoundEndpoint())
  216. .with_metadata<EndpointRoute>(new EndpointRoute("/not-found"));
  217. application.container.register_scoped<Endpoint>(() => new BadRequestEndpoint())
  218. .with_metadata<EndpointRoute>(new EndpointRoute("/bad-request"));
  219. application.container.register_scoped<Endpoint>(() => new InternalErrorEndpoint())
  220. .with_metadata<EndpointRoute>(new EndpointRoute("/internal-error"));
  221. application.container.register_scoped<Endpoint>(() => new ValidationEndpoint())
  222. .with_metadata<EndpointRoute>(new EndpointRoute("/validation"));
  223. application.container.register_scoped<Endpoint>(() => new ResourceEndpoint())
  224. .with_metadata<EndpointRoute>(new EndpointRoute("/resource/{id}"));
  225. application.container.register_scoped<Endpoint>(() => new ApiEndpoint())
  226. .with_metadata<EndpointRoute>(new EndpointRoute("/api/{endpoint}"));
  227. application.container.register_scoped<Endpoint>(() => new CustomErrorEndpoint())
  228. .with_metadata<EndpointRoute>(new EndpointRoute("/custom-error"));
  229. application.container.register_scoped<Endpoint>(() => new TimeoutEndpoint())
  230. .with_metadata<EndpointRoute>(new EndpointRoute("/timeout"));
  231. application.container.register_scoped<Endpoint>(() => new UnauthorizedEndpoint())
  232. .with_metadata<EndpointRoute>(new EndpointRoute("/unauthorized"));
  233. application.container.register_scoped<Endpoint>(() => new ForbiddenEndpoint())
  234. .with_metadata<EndpointRoute>(new EndpointRoute("/forbidden"));
  235. application.container.register_scoped<Endpoint>(() => new MethodNotAllowedEndpoint())
  236. .with_metadata<EndpointRoute>(new EndpointRoute("/method-not-allowed"));
  237. print("Error Handling Demo Server running on port 8088\n");
  238. print("Try these endpoints:\n");
  239. print(" - http://localhost:8088/\n");
  240. print(" - http://localhost:8088/ok\n");
  241. print(" - http://localhost:8088/not-found\n");
  242. print(" - http://localhost:8088/bad-request\n");
  243. print(" - http://localhost:8088/internal-error\n");
  244. print(" - http://localhost:8088/validation?email=test&age=25\n");
  245. print(" - http://localhost:8088/resource/1\n");
  246. print(" - http://localhost:8088/resource/999\n");
  247. print(" - http://localhost:8088/api/users\n");
  248. print(" - http://localhost:8088/custom-error?code=TEST&message=Test+error\n");
  249. print(" - http://localhost:8088/timeout\n");
  250. print(" - http://localhost:8088/unauthorized\n");
  251. print(" - http://localhost:8088/forbidden\n");
  252. print(" - http://localhost:8088/method-not-allowed\n");
  253. application.run();
  254. }
  255. // Helper functions
  256. HttpResult error_response(string message, StatusCode status) {
  257. var json = @"{ \"error\": \"Error\", \"message\": \"$message\" }";
  258. return new HttpStringResult(json)
  259. .set_header("Content-Type", "application/json");
  260. }
  261. string generate_request_id() {
  262. var timestamp = new DateTime.now_local().to_unix();
  263. var random = Random.next_int();
  264. return @"req-$timestamp-$random";
  265. }
  266. bool is_valid_email(string email) {
  267. // Simple email validation
  268. return email.contains("@") && email.contains(".") && email.length > 5;
  269. }
  270. // Helper classes
  271. class Resource {
  272. public int id { get; private set; }
  273. public string name { get; private set; }
  274. public string description { get; private set; }
  275. public Resource(int id, string name, string description) {
  276. this.id = id;
  277. this.name = name;
  278. this.description = description;
  279. }
  280. public string to_json() {
  281. return @"{ \"id\": $id, \"name\": \"$name\", \"description\": \"$description\" }";
  282. }
  283. }