Přeskočit na hlavní obsah

The 2nd method - REST API client

Overview

  • This is the second way of communication:
    • The standard REST API calls of application domain services using HTTP methods
  • Each backend service has REST API endpoints
    • REST API is defined by OPEN API 3.0 standard
      • Supported by 3rd-party tools like AutoRest, NSwag, editor.swagger.io etc
    • The most common endpoints supported by C# Connector nuget package

See more: OIDC/OAuth2 authentication

rest-api-consumer.png

  • Example - how to consume REST API:
    • Use HTTP client to call API endpoint you have chosen
    • See in example:
      • Controllers/Examples12Controller.cs
        • Example2_GetOrders_Using_3rdPartySupportOnly
        • Example2_GetOrders_Using_StandardConnector

Get orders using self-made connector based on Microsoft .NET and 3rd-party packages

  • The demonstration example how to integrate with other backend service running on AVAplace
  • Important: This code doesn't fully cover all requirements for more complex integration scenarios
  • See in example:
    • CustomConnectors/Example2_SelfMade3rdPartyBasedOrderClient.cs
    • Controllers/Examples12Controller.cs
      • Example2_GetOrders_Using_3rdPartySupportOnly

Usage example:

private readonly IServiceProvider _serviceProvider;
private readonly SsoAuthOptions _authOptions;
private readonly Uri _authBaseUri;
private readonly Uri _apiBaseUri;

/// <summary>
/// Example of common rest api call against platform with manual token handling.
/// This example isn't recommended to be used in production environment, it's only for demonstration purposes.
/// The example uses particular endpoint of PlatformStore.Order domain which is well-known with partner's application.
/// </summary>
public async Task<string> GetOrdersAsync(string tenantId, string orderAccessType, int offset = 0, int limit = 100, CancellationToken ct = default)
{
if (string.IsNullOrEmpty(orderAccessType)) throw new ArgumentNullException(nameof(orderAccessType));
ct.ThrowIfCancellationRequested();

// *** 1. authenticate ***
//try get access token from "Authorization" header of HTTP request
var httpContextAccessor = _serviceProvider.GetService<IHttpContextAccessor>();
string headerValue = httpContextAccessor?.HttpContext?.Request?.Headers[HeaderNames.Authorization];
AuthenticationHeaderValue authHeader;
if (!string.IsNullOrEmpty(headerValue))
{
authHeader = AuthenticationHeaderValue.Parse(headerValue);
}
else
{
//create access token by IdentityProvider using clientId and clientSecret (OIDC)
var authClient = new HttpClient { BaseAddress = _authBaseUri };

// - get discovery document
var discoveryRequest = new DiscoveryDocumentRequest { Address = _authOptions.Authority };
var discoveryResponse = await authClient.GetDiscoveryDocumentAsync(discoveryRequest, cancellationToken: ct);
if (discoveryResponse.IsError)
{
throw new InvalidOperationException($"'{discoveryResponse.Error}' - '{discoveryResponse.ErrorType}'", discoveryResponse.Exception);
}

// - get access token using client credentials
var tokenRequest = new ClientCredentialsTokenRequest
{
Address = discoveryResponse.TokenEndpoint,
ClientId = _authOptions.ClientId,
ClientSecret = _authOptions.ClientSecret,
Scope = _authOptions.Audience
};

// - append requested tenantId (optional)
if (!string.IsNullOrEmpty(tenantId))
{
tokenRequest.Parameters.Add("tid", tenantId);
}

// - call REST API endpoint of ASOL.IdentityProvider service
var tokenResponse = await authClient.RequestClientCredentialsTokenAsync(tokenRequest, ct);
if (tokenResponse.IsError)
{
throw new InvalidOperationException($"'{tokenResponse.Error}' - '{tokenResponse.ErrorDescription}'", discoveryResponse.Exception);
}

authHeader = new AuthenticationHeaderValue(tokenResponse.TokenType, tokenResponse.AccessToken);
}

// *** 2. call endpoint ***
//call REST API endpoint of ASOL.PlatformStore.Order service
var resource = "api/v1/Order";
var apiClient = new FluentClient(_apiBaseUri);
var request = apiClient.GetAsync(resource)
.WithAuthentication(authHeader.Scheme, authHeader.Parameter)
.WithCancellationToken(ct)
.WithArgument("orderAccessType", orderAccessType)
.WithArgument("offset", offset)
.WithArgument("limit", limit);

//return CollectionResult<OrderModel> contract as serialized JSON string
var result = await request.AsString();
return result;
}

Get orders using standard connector using ASOL.PlatformStore.Order.Connector package

  • The most comfortable way to integrate with other backend service running on AVAplace
  • The most preferred way for production environment
  • See in example:
    • Controllers/Examples12Controller.cs
      • Example2_GetOrders_Using_StandardConnector

Registration example (Startup.cs):

services.Configure<SsoAuthOptions>(configuration.GetSection(SsoAuthOptions.DefaultSectionName));
services.AddStandardApiTokenProviders();

services.Configure<PlatformStoreOrderClientOptions>(Configuration.GetSection(nameof(PlatformStoreOrderClientOptions)));
services.AddPlatformStoreOrderClient();

Usage example:

using ASOL.PlatformStore.Order.Connector;

//get client instance via dependency injection
var orderClient = _serviceProvider.GetRequiredService<IPlatformStoreOrderClient>();

//prepare query parameters
var orderFilter = new OrderFilter { OrderAccessType = OrderAccessType.Customer };

// *** 1. authenticate *** (already included in api call)
// *** 2. call endpoint ***
//get all orders via REST API client (using standard connector of ASOL.PlatformStore.Order service)
var ordersResult = await orderClient.GetOrdersAsync(orderFilter, default);