From 613cfc2f0fe899f240b51162a504a19114cbbd22 Mon Sep 17 00:00:00 2001 From: Pilzinsel64 Date: Thu, 17 Oct 2024 09:34:13 +0000 Subject: [PATCH] Pilz.Net erstellen --- Pilz.Net.md | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 Pilz.Net.md diff --git a/Pilz.Net.md b/Pilz.Net.md new file mode 100644 index 0000000..5f869c3 --- /dev/null +++ b/Pilz.Net.md @@ -0,0 +1,176 @@ +# WebApi + +`Pilz.Net.Api` introduces a very simple way to create a WebApi server and client. It's an alternative to the implemenation in ASP.NET which uses `Newtonsoft.Json` instead of `Json.NET` and is even more easier to implement and more extendable via plugins. As of now `Pilz.Net.Api` not fully REST compliant, this might be fixed in the future (PRs welcome). + +## How it works + +The process is simple: +1. The client sends a request with a given URL and an optional message to the server. +2. The server evaluates the request and response containing a status code and an optional a new message. +3. The client gets the response and *can* evaluate it, but doesn't need to. + +## Client + +### Create new ApiClient + +You can either use the pre-difined `ApiClient` or create your own by implementing `IApiClient`. Pass throw the api base url. + +```cs +var client = new ApiClient("http://localhost"); +``` + +### Make a request + +Creating a request can be done with the asyncron `IApiClient.MakeRequest()` and `IApiClient.MakeRequest()` methods. You need to pass throw the api url (relative to the api base url). Optionally you can also pass an `ApiMessage` instance or your own `IApiMessageSerializer`. + +```cs +// Run asyncron +var result = await client.SendRequest("/sdx/get"); + +// Run syncron +var result = client.SendRequest("/sdx/get").Result; +``` + +### Check result + +The result is always an instance of `ApiResponse` and contains the status code or the optional response message. + +You have several ways to check the status code: +- Check the status code manually via `StatusCode` for more detailed check. +- Check the status code via the method `IsOk()` which returns an boolean if the `StatusCode` is `Ok` and the `Message` is not null. +- Check the status code via the method `EnsureOk()` which throws an exception if the `StatusCode` is not `Ok` and `Message` is not null. + +```cs +// Check manually +If (result.StatusCode == HttpStatusCode.OK) + // ... +else + // ... + +// Check via IsOk() +If (result.IsOk()) + // ... +else + // ... + +// Check via EnsureOk() +result.EnsureOk(); +``` + +### Check message + +If the status code is fine and the message is not null, you can use it. It's typed as `T` while `T` is an type that inherits from `ApiMessage` which you can define at `SendRequest()` method. + +## Server + +### Create new ApiServer + +You can either use the pre-difined `ApiServer` or create your own by implementing `IApiServer`. Pass throw the api base url. + +```cs +var server = new Apiserver("http://localhost"); +``` + +Register your own handlers or let your plugins register their handlers anonymously. + +```cs +// Register object instance +// This registers all private and public methods with the `ApiMessageHandler` attribute. +server.RegisterHandler(new SDxApiServer()); + +// Register single method +server.Register(MyMethod); +``` + +### Create message handlers + +When a request reaches the server then the server checks each registered handler method for its `ApiMessageHandler` attribute. +- The return value of the method always needs to be an instance of `ApiResult`. +- The method itself can be private or public. +- The `ApiMessageHandler` defines the api url (relative to the api base url) and optionally if authentication is requied for this request and also an serializer type for deseralizing the ApiMessage. + +```cs +[ApiMessageHandler("/sdx/get")] +private ApiResult GetSDx() +{ + return ApiResult.Ok(new SDxResponse(Database.SDxList.Select(s => s.ToClient()))); +} +``` + +If your request can have an (optional) message, you can define a parameter with the target message type. + +```cs +[ApiMessageHandler("/sdx/get")] +private ApiResult GetSDx(SDxGetRequest message) +{ + if (message != null) + { + if (Database.SDxList.FirstOrDefault(s => s.Id == message.Id) is not SDx sdx) + return ApiResult.NotFound(); + return ApiResult.Ok(new SDxGetResponse(sdx); + } + return ApiResult.Ok(new SDxGetResponse(Database.SDxList.Select(s => s.ToClient()))); +} +``` + +If you need the whole request, you can define a parameter with the `ApiRequest` instance which also contains the `ApiMessage`, if available. However, you can even define both parameters at the same time, if you want. The `ApiRequest` can be useful if you want to check authentication conditionally depenting on the request message instead of defined `RequiesAuth = true` at the parameter. + +```cs +[ApiMessageHandler("/sdx/get")] +private ApiResult GetSDx(ApiRequest request, SDxGetRequest message) +{ + // ... +} +``` + +## Authentication + +For authentication you can either use the pre-defined events or create your own Server and Client class. Either inherit from `ApiServer`/`ApiClient` or create your complete own implementation by implement `IApiServer`/`IApiClient`. However, the `ApiServer` and `ApiClient` classes has some pre-defined methods you can overwrite to easily check the authentication. + +### Use events + +You can use the event for one-way and two-way encryption. + +#### Client + +Register this method to the `ApiClient.OnEcryptAuthKey` event. Encode the authKey (unencrypted) and return the decrypted value. + +```cs +protected virtual string? EncodeAuthKey() +{ + return AuthKey; +} +``` + +#### Server + +Register this method to the `ApiServer.OnCheckAuthentication` event. Decode the authKey (encrypted) and return the decrypted value. + +```cs +protected virtual string? DecodeAuthKey(string authKey) +{ + return authKey; +} +``` + +### Inherit & Overwrite + +You can use this way for one-way and two-way encryption. For one-way encryption you should overwrite `CheckAuthentication` instead of `DecodeAuthKey` and may not need `EncodeAuthKey` (depending on your security implementation. + +#### Client + +```cs +protected virtual string? EncodeAuthKey() +{ + return AuthKey; +} +``` + +#### Server + +```cs +protected virtual string? DecodeAuthKey(string authKey) +{ + return authKey; +} +``` \ No newline at end of file