Uniphar.Sap.Odata.Client
2.0.2
See the version list below for details.
dotnet add package Uniphar.Sap.Odata.Client --version 2.0.2
NuGet\Install-Package Uniphar.Sap.Odata.Client -Version 2.0.2
<PackageReference Include="Uniphar.Sap.Odata.Client" Version="2.0.2" />
<PackageVersion Include="Uniphar.Sap.Odata.Client" Version="2.0.2" />
<PackageReference Include="Uniphar.Sap.Odata.Client" />
paket add Uniphar.Sap.Odata.Client --version 2.0.2
#r "nuget: Uniphar.Sap.Odata.Client, 2.0.2"
#addin nuget:?package=Uniphar.Sap.Odata.Client&version=2.0.2
#tool nuget:?package=Uniphar.Sap.Odata.Client&version=2.0.2
sap-odata-client
NuGet package repository for Sap OData Client connection
Purpose
This library is for making calls to SAP (running in AZ). Calls to SAP require a base url. Full path will be decorated with the namespace of the service we call.
Calls to the SAP instance requires authentication with Basic auth
- (username
and password
). These are stored in a secret storage and you do not need to define these. The secret storage can be AZ Keyvault
, AWS Secrets Manager
or others. Any token
expiry is automatically handled by the SAP client and is not something to be concerned with either.
Custom Events
The library also publishes some custom telemetry events. This is because there has been several issues with the SAP Rise environments. We publich the following events
Event | Description | Remarks |
---|---|---|
SapTokenExpiredEvent | When x-csrf-token has expired, and we get a 403 back when calling the SAP Rise instance. |
|
SapTokenUnauthorizedFetchEvent | When requesting a new x-csrf-token SAP returns a 401 instead of the expected 403 |
The system will throw an UnauthorizedAccessException |
SapTokenRefreshedFetchEvent | When requesting a new x-csrf-token , and SAP returns anything but 401 , we notify it was part of the Response headers |
|
SapTokenNotFoundEvent | When requesting a new x-csrf-token , and SAP returns anything but 401 . However, response headers do not contain x-csrf-token |
Setup your project to use SapClient package
When using this NuGet package you need to setup a few things in your project
appsettings.json
Add this to appsettings.json
"SapClientOptions": {
"BaseUrl": "https://<sap-baseurl>",
"FetchTokenStatusCodeTriggers": [
401,
403
]
}
NOTE: : FetchTokenStatusCodeTriggers is a list of HttpCodes we test against to trigger fetching a new token. Normally it should just be 403, but we have to apply a hack to support 401 as well.
Signifgicant changes with version 2.0.2
UseHttpClientHandler
andPooledConnectionLifetime
have been retired with version 2.0.2, as we now use short-living http client instances created by HttpClientFactory. These are no longer part ofSapClientOptions
either.
Program.cs
Add the following to Program.cs
- Add this to bind the above configuration section to class
SapClientOptions
. That will ensure we can DI it into the SapClient class.
builder.Services.Configure<SapClientOptions>(
builder.Configuration.GetSection(nameof(SapClientOptions)));
- Register a singleton instance of HttpClient, as SapClient makes calls to SAP RISE over HTTP.
// If we need to use NullProxy
if (bool.Parse(builder.Configuration["InstanceConfig:EnableNullProxy"]!))
{
builder.Services.AddTransient<ISapClient, NullSapClient>();
}
else
{
// Register SAP client
builder.Services.AddTransient<ISapClient, SapClient>();
// Register HttpClient
builder.Services.AddHttpClient(nameof(SapClient), client =>
{
client.BaseAddress = new Uri(builder.Configuration["SapClientOptions:BaseUrl"]!);
client.Timeout = TimeSpan.Parse(builder.Configuration["SapClientOptions:Timeout"]!);
});
}
NOTE: : In the example above we have registered a named client as wse intend to use same configuration for all delegated clients when making request to SAP Rise.
When running locally
Add this in appsettings.Development.json
. That means we bypass any Cert logic when running locally (see above in registering the Http client)
"SapClientOptions": {
"UseHttpClientHandler": true
}
Using SapClient library
We have a couple of predefined (and custom) JsonSerialisers
we need to use when calling SAP. This is handled by class SapJsonSerializer
. It exposes a static method:
public static JsonSerializerOptions GetJsonSerializerOptions()
{
// preliminary code logic
jsonSerializerOptions.Converters.Add(new SapDateTimeConverter());
jsonSerializerOptions.Converters.Add(new SapDateTimeOffsetConverter());
jsonSerializerOptions.Converters.Add(new SapDecimalConverter());
return jsonSerializerOptions;
}
The SAP client package exposes two methods
Task<ContentResult> GetAsync<T>(string path, CancellationToken cancellationToken)
Task<ContentResult> PostAsync<T>(string path, T message, CancellationToken cancellationToken)
Both methods return a ContentResult
, which means we can handle based on the Http status code in the response.
Example usage:
public async Task<ActionResult<SalesOrderResponse>> CreateToDoList([FromBody] CreateToDoList createToDoList, CancellationToken cancellationToken)
{
// Call SAP Client to get Serializer Options
var serializerOptions = SapJsonSerializer.GetJsonSerializerOptions();
// Make call to SAP Client POST method
var createdTodoListContentResult = await _sapClient.PostAsync("TO_DO_LIST_API", createToDoList.MapToSapToDoListRequest(), cancellationToken);
switch(createdTodoListContentResult.StatusCode)
{
case 201:
var createdSapTodoListResult = JsonSerializer.Deserialize<OData<ToDoList>>(createdTodoListContentResult.Content!, serializerOptions);
// Make call to SAP Client GET method
var sapToDoListWithItems = await _sapClient.GetAsync<OData<ToDoList>>(
$"TO_DO_LIST_API/TO_DO_LIST_ITEM('{createdSapTodoListResult!.Data!.Id}')?$expand=to_Item", cancellationToken);
var sapToDoList = JsonSerializer.Deserialize<OData<ToDoList>>(sapToDoListWithItems.Content!, serializerOptions);
var newSapToDoList = sapToDoList!.Data!.MapToSapToDoListResponse();
return CreatedAtAction(nameof(CreateToDoList), new { id = newSalesOrder.Id }, newSalesOrder);
case 400:
var sapErrorResponse = JsonSerializer.Deserialize<SapErrorResponse>(createdTodoListContentResult.Content!, options);
return BadRequest(sapErrorResponse!.MapToProblemDetails());
default:
return new StatusCodeResult((int)createdTodoListContentResult.StatusCode!);
}
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.ApplicationInsights (>= 2.22.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
7.0.0 | 1,791 | 2/19/2025 | |
6.0.0 | 382 | 1/28/2025 | |
5.0.3 | 264 | 12/10/2024 | |
5.0.2 | 110 | 12/10/2024 | |
5.0.1 | 157 | 12/2/2024 | |
5.0.0 | 145 | 11/26/2024 | |
4.1.0 | 185 | 11/14/2024 | |
4.0.3 | 151 | 11/1/2024 | |
4.0.2 | 121 | 10/31/2024 | |
4.0.1 | 116 | 10/31/2024 | |
4.0.0 | 121 | 10/30/2024 | |
3.1.0 | 121 | 10/29/2024 | |
3.0.0 | 121 | 10/25/2024 | |
2.0.4 | 398 | 10/1/2024 | |
2.0.3 | 156 | 9/25/2024 | |
2.0.2 | 149 | 9/24/2024 | |
2.0.1 | 509 | 5/16/2024 | |
2.0.0 | 134 | 5/14/2024 | |
1.0.6 | 616 | 4/8/2024 | |
1.0.5 | 133 | 4/5/2024 | |
1.0.4 | 190 | 4/2/2024 | |
1.0.3 | 150 | 4/2/2024 | |
1.0.2 | 133 | 3/29/2024 | |
1.0.0 | 870 | 12/15/2023 | |
0.4.1 | 159 | 12/15/2023 | |
0.3.0 | 167 | 12/14/2023 | |
0.1.1 | 141 | 12/14/2023 | |
0.1.0 | 151 | 12/14/2023 |