ErrorHandling.vala 14 KB

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