Pilz.Net erstellen

2024-10-17 09:34:13 +00:00
parent b47c2c93ed
commit 613cfc2f0f

176
Pilz.Net.md Normal file

@@ -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<T>()` 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<SDxResponse>("/sdx/get");
// Run syncron
var result = client.SendRequest<SDxResponse>("/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<T>()` 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;
}
```