PathRouting.vala 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. using Astralis;
  2. using Invercargill;
  3. using Invercargill.DataStructures;
  4. /**
  5. * PathRouting Example
  6. *
  7. * Demonstrates path component parsing and routing in Astralis.
  8. * Uses the Router's named segment feature for dynamic routing.
  9. */
  10. // Root handler - shows available endpoints
  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. """Welcome to the Path Routing Example!
  15. Available endpoints:
  16. GET / - This message
  17. GET /hello - Simple greeting
  18. GET /hello/{name} - Greeting with name
  19. GET /users - List all users
  20. GET /users/{id} - Get user by ID
  21. GET /users/{id}/posts - Get posts for a user
  22. GET /api/v1/status - API status
  23. GET /api/v1/items/{id} - Get item by ID
  24. GET /files/{category}/{name} - Get file by category and name
  25. GET /pathinfo - Path information demo
  26. """
  27. );
  28. }
  29. }
  30. // Simple hello handler
  31. class HelloHandler : Object, RouteHandler {
  32. public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error {
  33. return new BufferedHttpResult.from_string("Hello, World!");
  34. }
  35. }
  36. // Hello with name handler - uses named segment
  37. class HelloNameHandler : Object, RouteHandler {
  38. public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error {
  39. var name = route_context.get_segment("name");
  40. return new BufferedHttpResult.from_string(@"Hello, $name!");
  41. }
  42. }
  43. // Users list handler
  44. class UsersHandler : Object, RouteHandler {
  45. public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error {
  46. var users = new Series<User>();
  47. users.add(new User(1, "Alice", "alice@example.com"));
  48. users.add(new User(2, "Bob", "bob@example.com"));
  49. users.add(new User(3, "Charlie", "charlie@example.com"));
  50. var json_parts = new Series<string>();
  51. json_parts.add(@"{ \"users\": [");
  52. bool first = true;
  53. users.to_immutable_buffer().iterate((user) => {
  54. if (!first) json_parts.add(", ");
  55. json_parts.add(user.to_json());
  56. first = false;
  57. });
  58. json_parts.add("] }");
  59. var json_string = json_parts.to_immutable_buffer()
  60. .aggregate<string>("", (acc, s) => acc + s);
  61. var headers = new Catalogue<string, string>();
  62. headers.add("Content-Type", "application/json");
  63. return new BufferedHttpResult.from_string(
  64. json_string,
  65. StatusCode.OK,
  66. headers
  67. );
  68. }
  69. }
  70. // User by ID handler - uses named segment
  71. class UserByIdHandler : Object, RouteHandler {
  72. public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error {
  73. var id_str = route_context.get_segment("id");
  74. var id = int.parse(id_str);
  75. // Simulated user lookup
  76. var users = new Series<User>();
  77. users.add(new User(1, "Alice", "alice@example.com"));
  78. users.add(new User(2, "Bob", "bob@example.com"));
  79. users.add(new User(3, "Charlie", "charlie@example.com"));
  80. var user = users.to_immutable_buffer()
  81. .first_or_default(u => u.id == id);
  82. if (user == null) {
  83. var headers = new Catalogue<string, string>();
  84. headers.add("Content-Type", "application/json");
  85. return new BufferedHttpResult.from_string(
  86. @"{ \"error\": \"User not found\" }",
  87. StatusCode.NOT_FOUND,
  88. headers
  89. );
  90. }
  91. var headers = new Catalogue<string, string>();
  92. headers.add("Content-Type", "application/json");
  93. return new BufferedHttpResult.from_string(
  94. user.to_json(),
  95. StatusCode.OK,
  96. headers
  97. );
  98. }
  99. }
  100. // User posts handler - uses named segment
  101. class UserPostsHandler : Object, RouteHandler {
  102. public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error {
  103. var id_str = route_context.get_segment("id");
  104. var id = int.parse(id_str);
  105. // Simulated posts for user
  106. var posts = new Series<Post>();
  107. posts.add(new Post(101, id, "First Post", "This is my first post"));
  108. posts.add(new Post(102, id, "Second Post", "This is my second post"));
  109. var json_parts = new Series<string>();
  110. json_parts.add(@"{ \"user_id\": $id, \"posts\": [");
  111. bool first = true;
  112. posts.to_immutable_buffer().iterate((post) => {
  113. if (!first) json_parts.add(", ");
  114. json_parts.add(post.to_json());
  115. first = false;
  116. });
  117. json_parts.add("] }");
  118. var json_string = json_parts.to_immutable_buffer()
  119. .aggregate<string>("", (acc, s) => acc + s);
  120. var headers = new Catalogue<string, string>();
  121. headers.add("Content-Type", "application/json");
  122. return new BufferedHttpResult.from_string(
  123. json_string,
  124. StatusCode.OK,
  125. headers
  126. );
  127. }
  128. }
  129. // API status handler
  130. class ApiStatusHandler : Object, RouteHandler {
  131. public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error {
  132. var status = new Dictionary<string, string>();
  133. status.set("status", "operational");
  134. status.set("version", "1.0.0");
  135. status.set("timestamp", new DateTime.now_local().format_iso8601());
  136. var json_parts = new Series<string>();
  137. json_parts.add("{ ");
  138. bool first = true;
  139. status.to_immutable_buffer().iterate((kv) => {
  140. if (!first) json_parts.add(", ");
  141. json_parts.add(@"\"$(kv.key)\": \"$(kv.value)\"");
  142. first = false;
  143. });
  144. json_parts.add(" }");
  145. var json_string = json_parts.to_immutable_buffer()
  146. .aggregate<string>("", (acc, s) => acc + s);
  147. var headers = new Catalogue<string, string>();
  148. headers.add("Content-Type", "application/json");
  149. return new BufferedHttpResult.from_string(
  150. json_string,
  151. StatusCode.OK,
  152. headers
  153. );
  154. }
  155. }
  156. // API item handler - uses named segment
  157. class ApiItemHandler : Object, RouteHandler {
  158. public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error {
  159. var id_str = route_context.get_segment("id");
  160. var id = int.parse(id_str);
  161. var items = new Dictionary<int, Item>();
  162. items.set(1, new Item(1, "Widget", "A useful widget", 9.99));
  163. items.set(2, new Item(2, "Gadget", "A fancy gadget", 19.99));
  164. items.set(3, new Item(3, "Doohickey", "A mysterious doohickey", 29.99));
  165. Item? item = null;
  166. if (items.try_get(id, out item)) {
  167. var headers = new Catalogue<string, string>();
  168. headers.add("Content-Type", "application/json");
  169. return new BufferedHttpResult.from_string(
  170. item.to_json(),
  171. StatusCode.OK,
  172. headers
  173. );
  174. }
  175. var headers = new Catalogue<string, string>();
  176. headers.add("Content-Type", "application/json");
  177. return new BufferedHttpResult.from_string(
  178. @"{ \"error\": \"Item not found\" }",
  179. StatusCode.NOT_FOUND,
  180. headers
  181. );
  182. }
  183. }
  184. // Files handler - uses two named segments
  185. class FilesHandler : Object, RouteHandler {
  186. public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error {
  187. var category = route_context.get_segment("category");
  188. var name = route_context.get_segment("name");
  189. // Simulated file lookup
  190. var files = new Dictionary<string, Dictionary<string, ExampleFile>>();
  191. var docs = new Dictionary<string, ExampleFile>();
  192. docs.set("readme.txt", new ExampleFile("readme.txt", "docs", "This is the readme file"));
  193. docs.set("guide.pdf", new ExampleFile("guide.pdf", "docs", "User guide"));
  194. var images = new Dictionary<string, ExampleFile>();
  195. images.set("logo.png", new ExampleFile("logo.png", "images", "Company logo"));
  196. images.set("banner.jpg", new ExampleFile("banner.jpg", "images", "Website banner"));
  197. files.set("docs", docs);
  198. files.set("images", images);
  199. Dictionary<string, ExampleFile>? category_files = null;
  200. if (files.try_get(category, out category_files)) {
  201. ExampleFile? file = null;
  202. if (category_files.try_get(name, out file)) {
  203. var headers = new Catalogue<string, string>();
  204. headers.add("Content-Type", "application/json");
  205. return new BufferedHttpResult.from_string(
  206. file.to_json(),
  207. StatusCode.OK,
  208. headers
  209. );
  210. }
  211. }
  212. var headers = new Catalogue<string, string>();
  213. headers.add("Content-Type", "application/json");
  214. return new BufferedHttpResult.from_string(
  215. @"{ \"error\": \"File not found in category '$category'\" }",
  216. StatusCode.NOT_FOUND,
  217. headers
  218. );
  219. }
  220. }
  221. // Path info handler
  222. class PathInfoHandler : Object, RouteHandler {
  223. public async HttpResult handle_route(HttpContext http_context, RouteContext route_context) throws Error {
  224. var parts = new Series<string>();
  225. parts.add("Path Information:\n");
  226. parts.add(@" Raw path: $(http_context.request.raw_path)\n");
  227. parts.add(@" Path components: $(http_context.request.path_components.count())\n");
  228. parts.add("\n Components:\n");
  229. http_context.request.path_components
  230. .with_positions()
  231. .iterate((pair) => {
  232. parts.add(@" [$((int)pair.position)]: $(pair.item)\n");
  233. });
  234. parts.add(@"\n Query string: $(http_context.request.query_string)\n");
  235. // Show named segments from route context
  236. parts.add("\n Named segments:\n");
  237. route_context.named_segments.to_immutable_buffer()
  238. .iterate((kv) => {
  239. parts.add(@" $(kv.key): $(kv.value)\n");
  240. });
  241. var result = parts.to_immutable_buffer()
  242. .aggregate<string>("", (acc, s) => acc + s);
  243. return new BufferedHttpResult.from_string(result);
  244. }
  245. }
  246. void main() {
  247. var router = new Router();
  248. var server = new Server(8082, router);
  249. // Register routes with named segments
  250. router.get("/", new RootHandler());
  251. router.get("/hello", new HelloHandler());
  252. router.get("/hello/{name}", new HelloNameHandler());
  253. router.get("/users", new UsersHandler());
  254. router.get("/users/{id}", new UserByIdHandler());
  255. router.get("/users/{id}/posts", new UserPostsHandler());
  256. router.get("/api/v1/status", new ApiStatusHandler());
  257. router.get("/api/v1/items/{id}", new ApiItemHandler());
  258. router.get("/files/{category}/{name}", new FilesHandler());
  259. router.get("/pathinfo", new PathInfoHandler());
  260. print("Path Routing Example Server running on port 8082\n");
  261. print("Try these endpoints:\n");
  262. print(" - http://localhost:8082/\n");
  263. print(" - http://localhost:8082/hello\n");
  264. print(" - http://localhost:8082/hello/Alice\n");
  265. print(" - http://localhost:8082/users\n");
  266. print(" - http://localhost:8082/users/1\n");
  267. print(" - http://localhost:8082/users/1/posts\n");
  268. print(" - http://localhost:8082/api/v1/status\n");
  269. print(" - http://localhost:8082/api/v1/items/2\n");
  270. print(" - http://localhost:8082/files/docs/readme.txt\n");
  271. print(" - http://localhost:8082/pathinfo?test=1\n");
  272. server.run();
  273. }
  274. // Helper classes for the example
  275. class User {
  276. public int id { get; private set; }
  277. public string name { get; private set; }
  278. public string email { get; private set; }
  279. public User(int id, string name, string email) {
  280. this.id = id;
  281. this.name = name;
  282. this.email = email;
  283. }
  284. public string to_json() {
  285. return @"{ \"id\": $id, \"name\": \"$name\", \"email\": \"$email\" }";
  286. }
  287. }
  288. class Post {
  289. public int id { get; private set; }
  290. public int user_id { get; private set; }
  291. public string title { get; private set; }
  292. public string content { get; private set; }
  293. public Post(int id, int user_id, string title, string content) {
  294. this.id = id;
  295. this.user_id = user_id;
  296. this.title = title;
  297. this.content = content;
  298. }
  299. public string to_json() {
  300. return @"{ \"id\": $id, \"user_id\": $user_id, \"title\": \"$title\", \"content\": \"$content\" }";
  301. }
  302. }
  303. class Item {
  304. public int id { get; private set; }
  305. public string name { get; private set; }
  306. public string description { get; private set; }
  307. public double price { get; private set; }
  308. public Item(int id, string name, string description, double price) {
  309. this.id = id;
  310. this.name = name;
  311. this.description = description;
  312. this.price = price;
  313. }
  314. public string to_json() {
  315. return @"{ \"id\": $id, \"name\": \"$name\", \"description\": \"$description\", \"price\": $price }";
  316. }
  317. }
  318. class ExampleFile {
  319. public string name { get; private set; }
  320. public string category { get; private set; }
  321. public string description { get; private set; }
  322. public ExampleFile(string name, string category, string description) {
  323. this.name = name;
  324. this.category = category;
  325. this.description = description;
  326. }
  327. public string to_json() {
  328. return @"{ \"name\": \"$name\", \"category\": \"$category\", \"description\": \"$description\" }";
  329. }
  330. }