SimpleREST.NET 0.1
A simple and minimal api framework for .net base on Express.js
Loading...
Searching...
No Matches
SimpleRestApi.cs
Go to the documentation of this file.
1using System.Diagnostics;
2using System.Net;
3using Dumpify;
4using System.Text.Json;
6using Spectre.Console;
7using UriTemplate.Core;
8using Uri = UriTemplate.Core;
9
10namespace SimpleRest.Api;
11
12public delegate Task ApiMiddleWare(SimpleRestRequest request, SimpleRestResponse response);
13
14public class SimpleRestApi : IDisposable
15{
16 ISimpleRestLogger m_Logger;
17 ISimpleRestContentTypeParser m_ResponseTypeParser;
18 ISimpleRestUriTemplateFormatter m_UriTemplateFormatter;
19 ISimpleRestEndpointFormatter m_EndpointFormatter;
20 List<ISimpleRestApiHandler> m_Handlers = new List<ISimpleRestApiHandler>();
21 HttpListener m_Listener;
22 int m_Port;
23
24 List<SimpleRestMap> m_Middleware = new List<SimpleRestMap>();
25 Type m_DefaultIntType;
26 JsonSerializerOptions m_SerializerOptions;
27 public bool HasStarted { get; private set; } = false;
28 public bool Disposed { get; private set; } = false;
29 public event Action<SimpleRestApi>? OnServerStart;
30 public event Action<SimpleRestApi>? OnBeforeRequestCreate;
31 public event Action<SimpleRestApi, SimpleRestRequest>? OnRequestCreate;
32 public event Action<SimpleRestApi, SimpleRestRequest>? OnBeforeResponseCreate;
33 public event Action<SimpleRestApi, SimpleRestRequest, SimpleRestResponse>? OnResponseCreate;
34 public event Action<SimpleRestApi, SimpleRestRequest>? OnLog;
35 public event Action<
39 Dictionary<UriTemplateMatch, SimpleRestMap>
41 public event Action<
45 UriTemplateMatch,
48 public event Action<
52 UriTemplateMatch,
55 public event Action<
59 UriTemplateMatch,
62 public event Action<
66 UriTemplateMatch,
69 public event Action<SimpleRestApi, SimpleRestRequest, SimpleRestResponse>? OnBeforeRequestEnd;
70 public event Action<SimpleRestApi, SimpleRestRequest, SimpleRestResponse>? OnRequestEnd;
71
73 int port,
74 ISimpleRestLogger? logger = null,
75 ISimpleRestContentTypeParser? responseParser = null,
76 ISimpleRestUriTemplateFormatter? uriFormatter = null,
77 ISimpleRestEndpointFormatter? endpointFormatter = null,
78 JsonSerializerOptions? jsonSerializerOptions = null,
79 Type? defaultIntType = null
80 )
81 {
82 m_Logger = logger ?? new SimpleRestLogger();
83 m_ResponseTypeParser = responseParser ?? new SimpleRestContentTypeParser();
84 m_Listener = new HttpListener();
85 m_UriTemplateFormatter = uriFormatter ?? new SimpleRestUriTemplateHandler();
86 m_Port = port;
87 m_Listener.Prefixes.Add("http://*:" + port + "/");
88 m_SerializerOptions =
89 jsonSerializerOptions
90 ?? new JsonSerializerOptions
91 {
92 PropertyNameCaseInsensitive = true,
93 PropertyNamingPolicy = JsonNamingPolicy.CamelCase
94 };
95 m_EndpointFormatter = endpointFormatter ?? new SimpleRestEndpointFormatter();
96 m_DefaultIntType = defaultIntType ?? typeof(int);
97 }
98
99 void MapApiHandlers()
100 {
101 foreach (ISimpleRestApiHandler handler in m_Handlers)
102 {
103 OnServerStart += handler.OnServerStart;
108 OnLog += handler.OnLog;
115 OnRequestEnd += handler.OnRequestEnd;
116 }
117 }
118
119 // void MapRouteHandlers()
120 // {
121 // foreach (SimpleRestMap map in m_Middleware)
122 // {
123 // map.RouteHandlers.ToList()
124 // .ForEach(h =>
125 // {
126 // OnRequestCreate += h.OnRequest;
127 // OnResponseCreate += h.OnResponseCreate;
128 // OnRequestEnd += h.OnResponse;
129 // });
130 // }
131 // }
132
133 void AddMiddleware(
134 string endpoint,
135 SimpleRestMethod method,
136 ApiMiddleWare middleWare,
137 ISimpleRestRouteHandler[]? routeHandlers = null
138 )
139 {
140 m_Middleware.Add(
141 new SimpleRestMap(endpoint, method, middleWare, m_UriTemplateFormatter, routeHandlers)
142 );
143 }
144
145 public void Use(ISimpleRestApiHandler customHandler)
146 {
147 m_Handlers.Add(customHandler);
148 }
149
150 public void Map(
151 string endpoint,
152 ApiMiddleWare middleWare,
153 params ISimpleRestRouteHandler[] routeHandlers
154 )
155 {
156 AddMiddleware(endpoint, SimpleRestMethod.ANY, middleWare, routeHandlers);
157 }
158
159 public void All(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
160 {
161 AddMiddleware("/*", SimpleRestMethod.ANY, middleWare, routeHandlers);
162 }
163
164 public void Options(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
165 {
166 AddMiddleware("/*", SimpleRestMethod.OPTIONS, middleWare, routeHandlers);
167 }
168
169 public void Options(
170 string endpoint,
171 ApiMiddleWare middleWare,
172 params ISimpleRestRouteHandler[] routeHandlers
173 )
174 {
175 AddMiddleware(endpoint, SimpleRestMethod.OPTIONS, middleWare, routeHandlers);
176 }
177
178 public void Get(
179 string endpoint,
180 ApiMiddleWare middleWare,
181 params ISimpleRestRouteHandler[] routeHandlers
182 )
183 {
184 AddMiddleware(endpoint, SimpleRestMethod.GET, middleWare, routeHandlers);
185 }
186
187 public void Get(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
188 {
189 AddMiddleware("/*", SimpleRestMethod.GET, middleWare, routeHandlers);
190 }
191
192 public void Post(
193 string endpoint,
194 ApiMiddleWare middleWare,
195 params ISimpleRestRouteHandler[] routeHandlers
196 )
197 {
198 AddMiddleware(endpoint, SimpleRestMethod.POST, middleWare, routeHandlers);
199 }
200
201 public void Post(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
202 {
203 AddMiddleware("/*", SimpleRestMethod.POST, middleWare, routeHandlers);
204 }
205
206 public void Put(
207 string endpoint,
208 ApiMiddleWare middleWare,
209 params ISimpleRestRouteHandler[] routeHandlers
210 )
211 {
212 AddMiddleware(endpoint, SimpleRestMethod.PUT, middleWare, routeHandlers);
213 }
214
215 public void Put(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
216 {
217 AddMiddleware("/*", SimpleRestMethod.PUT, middleWare, routeHandlers);
218 }
219
220 public void Patch(
221 string endpoint,
222 ApiMiddleWare middleWare,
223 params ISimpleRestRouteHandler[] routeHandlers
224 )
225 {
226 AddMiddleware(endpoint, SimpleRestMethod.PATCH, middleWare, routeHandlers);
227 }
228
229 public void Patch(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
230 {
231 AddMiddleware("/*", SimpleRestMethod.PATCH, middleWare, routeHandlers);
232 }
233
234 public void Delete(
235 string endpoint,
236 ApiMiddleWare middleWare,
237 params ISimpleRestRouteHandler[] routeHandlers
238 )
239 {
240 AddMiddleware(endpoint, SimpleRestMethod.DELETE, middleWare, routeHandlers);
241 }
242
243 public void Delete(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
244 {
245 AddMiddleware("/*", SimpleRestMethod.DELETE, middleWare, routeHandlers);
246 }
247
248 public void Head(
249 string endpoint,
250 ApiMiddleWare middleWare,
251 params ISimpleRestRouteHandler[] routeHandlers
252 )
253 {
254 AddMiddleware(endpoint, SimpleRestMethod.HEAD, middleWare, routeHandlers);
255 }
256
257 public void Head(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
258 {
259 AddMiddleware("/*", SimpleRestMethod.HEAD, middleWare, routeHandlers);
260 }
261
267 public async Task Start(Action<int, string>? OnStartup = null)
268 {
269 try
270 {
271 m_Listener.Start();
272 MapApiHandlers();
273 // MapRouteHandlers();
274 m_Middleware.Dump();
275 OnServerStart?.Invoke(this);
276 OnStartup?.Invoke(m_Port, m_Listener.Prefixes.First().Replace("*", "localhost"));
277 HasStarted = true;
278 Disposed = false;
279 while (!Disposed)
280 {
281 try
282 {
283 HttpListenerContext context = await m_Listener.GetContextAsync();
284 OnBeforeRequestCreate?.Invoke(this);
285
287 context,
288 m_SerializerOptions, m_EndpointFormatter
289 );
290 OnRequestCreate?.Invoke(this, request);
291 OnBeforeResponseCreate?.Invoke(this, request);
293 context.Response,
294 m_ResponseTypeParser, m_SerializerOptions
295 );
296 OnResponseCreate?.Invoke(this, request, response);
297
298 m_Logger.Log(request);
299 OnLog?.Invoke(this, request);
300
301 await RunMiddleWare(request, response);
302 if (response.HasCompleted)
303 continue;
304 OnBeforeRequestEnd?.Invoke(this, request, response);
305 response.Return();
306 OnRequestEnd?.Invoke(this, request, response);
307 }
308 catch (ObjectDisposedException ode)
309 {
310 Console.WriteLine("Server disposed");
311 AnsiConsole.WriteException(ode);
312 return;
313 }
314 catch (Exception e)
315 {
316 Console.WriteLine(
317 "Something went wrong while getting request: "
318 + e.Message
319 + " "
320 + e.StackTrace
321 );
322 }
323 }
324 }
325 catch (Exception e)
326 {
327 Console.WriteLine("Failure to open server: " + e.Message);
328 }
329 }
330
331 async Task RunMiddleWare(SimpleRestRequest request, SimpleRestResponse response)
332 {
333 Dictionary<UriTemplateMatch, SimpleRestMap> matches = m_Middleware
334 .Where(m =>
335 m.Pattern.Match(new System.Uri(request.Endpoint, UriKind.Relative)) != null
336 || m.Endpoint == "/*"
337 )
338 .ToDictionary(
339 m =>
340 m.Pattern.Match(new System.Uri(request.Endpoint, UriKind.Relative))
341 ?? new Uri.UriTemplate("/*").Match(new System.Uri("/*")),
342 m => m
343 );
344 OnHandleRequestStack?.Invoke(this, request, response, matches);
345
346 foreach (KeyValuePair<UriTemplateMatch, SimpleRestMap> match in matches)
347 {
348 if (response.HasCompleted)
349 return;
350 SimpleRestMap map = match.Value;
351 UriTemplateMatch uriTemplateMatch = match.Key;
352
353 if (map.Method == SimpleRestMethod.ANY || map.Method == request.Method)
354 {
355
356 OnRequestMatch?.Invoke(this, request, response, uriTemplateMatch, map);
357 ApplyUriParams(uriTemplateMatch, request);
358 OnApplyUriParams?.Invoke(this, request, response, uriTemplateMatch, map);
359 map.RouteHandlers?.ToList()
360 .ForEach(h =>
361 {
362 h.OnRequest(this, request, response);
363 response.OnSend += (result) =>
364 {
365 h.OnResponse(this, request, response);
366 };
367 });
368
369 OnBeforeRunMiddleware?.Invoke(this, request, response, uriTemplateMatch, map);
370 await map.Middleware.Invoke(request, response);
371 OnRunMiddleware?.Invoke(this, request, response, uriTemplateMatch, map);
372 }
373 }
374 }
375
376 void ApplyUriParams(UriTemplateMatch match, SimpleRestRequest request)
377 {
378 Dictionary<string, object?> paramsToAdd = new Dictionary<string, object?>();
379 foreach (string key in match.Bindings.Keys)
380 {
381 paramsToAdd.Add(key, match.Bindings[key].Value);
382 object? converted = null;
383 try
384 {
385 converted = JsonSerializer.Deserialize<object?>(
386 match.Bindings[key].Value.ToString() ?? "null"
387 );
388 }
389 catch (JsonException je)
390 {
391 string stringifiedObject = $"\"{match.Bindings[key].Value.ToString()}\"";
392 converted = JsonSerializer.Deserialize<object?>(stringifiedObject ?? "null");
393 }
394 converted = ApplyIntType(converted);
395 paramsToAdd[key] = converted;
396 }
397 request.Params.NonDistructiveUnion(paramsToAdd);
398 }
399
400 object? ApplyIntType(object? value)
401 {
402 if (
403 value != null && value is short
404 || value is ushort
405 || value is int
406 || value is uint
407 || value is long
408 || value is ulong
409 )
410 {
411 return Convert.ChangeType(value, m_DefaultIntType);
412 }
413 return value;
414 }
415
416 public void Stop()
417
418
419 {
420 Disposed = true;
421
422 m_Listener.Stop();
423 }
424
425 public void Dispose()
426 {
427 Stop();
428 }
429}
UriTemplate.Core Uri
void Post(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
async Task Start(Action< int, string >? OnStartup=null)
void Get(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
Action< SimpleRestApi >? OnBeforeRequestCreate
void All(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
Action< SimpleRestApi, SimpleRestRequest, SimpleRestResponse >? OnBeforeRequestEnd
void Options(string endpoint, ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
Action< SimpleRestApi, SimpleRestRequest >? OnRequestCreate
void Post(string endpoint, ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
void Options(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
void Delete(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
Action< SimpleRestApi, SimpleRestRequest, SimpleRestResponse >? OnRequestEnd
Action< SimpleRestApi, SimpleRestRequest, SimpleRestResponse, UriTemplateMatch, SimpleRestMap >? OnApplyUriParams
Action< SimpleRestApi, SimpleRestRequest >? OnBeforeResponseCreate
Action< SimpleRestApi, SimpleRestRequest, SimpleRestResponse, UriTemplateMatch, SimpleRestMap >? OnBeforeRunMiddleware
Action< SimpleRestApi, SimpleRestRequest, SimpleRestResponse >? OnResponseCreate
void Head(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
Action< SimpleRestApi >? OnServerStart
Action< SimpleRestApi, SimpleRestRequest >? OnLog
void Put(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
Action< SimpleRestApi, SimpleRestRequest, SimpleRestResponse, UriTemplateMatch, SimpleRestMap >? OnRunMiddleware
Action< SimpleRestApi, SimpleRestRequest, SimpleRestResponse, UriTemplateMatch, SimpleRestMap >? OnRequestMatch
SimpleRestApi(int port, ISimpleRestLogger? logger=null, ISimpleRestContentTypeParser? responseParser=null, ISimpleRestUriTemplateFormatter? uriFormatter=null, ISimpleRestEndpointFormatter? endpointFormatter=null, JsonSerializerOptions? jsonSerializerOptions=null, Type? defaultIntType=null)
void Use(ISimpleRestApiHandler customHandler)
Action< SimpleRestApi, SimpleRestRequest, SimpleRestResponse, Dictionary< UriTemplateMatch, SimpleRestMap > >? OnHandleRequestStack
void Get(string endpoint, ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
void Put(string endpoint, ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
void Map(string endpoint, ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
void Patch(string endpoint, ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
void Head(string endpoint, ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
void Delete(string endpoint, ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
void Patch(ApiMiddleWare middleWare, params ISimpleRestRouteHandler[] routeHandlers)
ISimpleRestRouteHandler?[] RouteHandlers
The main class for all incoming request data. This is a wrapper for the HttpListenerRequest class....
static SimpleRestRequest FromHttpListenerContext(HttpListenerContext listenerContext, JsonSerializerOptions jsonOptions, ISimpleRestEndpointFormatter? endpointFormatter=null)
Factory function creating a Request object with all http request info stored on it.
void OnBeforeRequestEnd(SimpleRestApi api, SimpleRestRequest request, SimpleRestResponse response)
void OnRequestMatch(SimpleRestApi api, SimpleRestRequest request, SimpleRestResponse response, UriTemplateMatch match, SimpleRestMap routeMap)
void OnRequestCreate(SimpleRestApi api, SimpleRestRequest request)
void OnResponseCreate(SimpleRestApi api, SimpleRestRequest request, SimpleRestResponse response)
void OnRunMiddleware(SimpleRestApi api, SimpleRestRequest request, SimpleRestResponse response, UriTemplateMatch match, SimpleRestMap routeMap)
void OnRequestEnd(SimpleRestApi api, SimpleRestRequest request, SimpleRestResponse response)
void OnLog(SimpleRestApi api, SimpleRestRequest request)
void OnBeforeRunMiddleware(SimpleRestApi api, SimpleRestRequest request, SimpleRestResponse response, UriTemplateMatch match, SimpleRestMap routeMap)
void OnBeforeRequestCreate(SimpleRestApi api)
void OnHandleRequestStack(SimpleRestApi api, SimpleRestRequest request, SimpleRestResponse response, Dictionary< UriTemplateMatch, SimpleRestMap > matches)
void OnApplyUriParams(SimpleRestApi api, SimpleRestRequest request, SimpleRestResponse response, UriTemplateMatch match, SimpleRestMap routeMap)
void OnBeforeResponseCreate(SimpleRestApi api, SimpleRestRequest request)
void OnServerStart(SimpleRestApi api)
void Log(string customMessage, SimpleRestLogLevel? logLevel=null)
delegate Task ApiMiddleWare(SimpleRestRequest request, SimpleRestResponse response)