ErrorHandling.vala 14 KB

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