ErrorHandling.vala 12 KB

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