C# API Client
- 12 minutes to read
In this tutorial, we will show how you can use Customer's Canvas REST API with the help of the official C# API Client. As an example, we will create a simple controller that requests a list of designs.
We will assume that you already have an instance of Customer's Canvas set up and running on our infrastructure or your servers.
Preparation
Creating a test project
- In Visual Studio, create a new project, either ASP.NET Web Application (.NET Framework) or ASP.NET Core Web Application.
- When creating the project, use the Web API project template.
- Select the 64 bit version of IIS Express:
- In the menu, click Tools > Options > Projects and Solutions > Web Projects.
- Select Use the 64 bit version of IIS Express for web sites and projects.
Installing Nuget packages
The official C# API Client for Customer's Canvas is published in Nuget. Each service has its own package. For the purposes of our tutorial, let's install the Aurigma.AssetStorage.ApiClient
by typing the following command in the Visual Studio Package Manager Console:
Install-Package Aurigma.AssetStorage.ApiClient
Refer to API Reference for a full list of APIs.
Setting Up API Clients
To use a C# API client, it is necessary to set it up through a dependency injection mechanism. For .NET Core, it is a built-in dependency injection functionality, for .NET Framework we will use Autofac. You may use another IoC container if you prefer.
Find the ConfigureServices
method in Startup.cs. Here, we will put the API client configuration code:
public void ConfigureServices(IServiceCollection services) {
// ... We will add our code here
services.AddControllers();
}
Then add the configuration code like this:
services.AddScoped<Aurigma.AssetStorage.IApiClientConfiguration>(s =>
{
return new Aurigma.AssetStorage.ApiClientConfiguration()
{
ApiUrl = "https://assetstorageapi.example.com/",
// ... additional parameters will go here (see further)
};
});
// Here, we register all API clients we are going to use. A separate API client exists
// for every section you can find in the API reference and the name is formed as
// _SectionName_ApiClient (and I_SectionName_ApiClient for the interface name).
//
// For example, for the Designs, it will be DesignsApiClient and IDesignApiClient,
// while for the PrivateDesigns, it will be PrivateDesignsApiClient and
// IPrivateDesignsApiClient.
services.AddHttpClient<IDesignsApiClient, DesignsApiClient>();
services.AddHttpClient<IPrivateDesignsApiClient, PrivateDesignsApiClient>();
Note
To make successful API calls, it is necessary to authorize them. You can do so by passing an API key or a token received from BackOffice to the configuration. The different ways to authorize will be considered in more detail later. In the meantime, let's see how you can create and use instances of API clients in your controllers and services.
Using API
Once you set up your clients using dependency injection, just add the appropriate client interfaces to the constructor. There is a separate client for each section in the API Reference. The interface name is ISectionNameApiClient. For example, if you need a client for Designs, the interface name will be IDesignsApiClient
. It will have the same methods as you can see in the reference with the Async
prefix at the end.
Here is an example of a controller that uses API clients:
[ApiController]
[Route("/")]
public class SampleController : ControllerBase
{
private readonly IDesignsApiClient _client;
public SampleController(IDesignsApiClient client)
{
_client = client;
}
[HttpGet("action")]
public async Task<ActionResult> Action()
{
var result = await _client.GetAllAsync();
return Ok(result);
}
}
You can pass more than one interface. It is also possible to override API client settings you set up through dependency injection.
[ApiController]
[Route("/")]
public class Sample2Controller : ControllerBase
{
private readonly IDesignsApiClient _client;
private readonly IPrivateDesignsApiClient _privateClient;
public Sample2Controller(IDesignsApiClient client, IPrivateDesignsApiClient privateClient, IHttpContextAccessor httpContextAccessor)
{
_client = client;
_privateClient = privateClient;
// we can override client settings
var token = httpContextAccessor?.HttpContext?.Request
.Headers["Authorization"].ToString().Replace("Bearer ", "");
_client.AuthorizationToken = token;
_privateClient.AuthorizationToken = token;
}
[HttpGet("action")]
public async Task<ActionResult> Action()
{
var result = await _client.GetAllAsync();
return Ok(result);
}
}
Authorization
Now, let's return to the client setup options. The main thing you configure when setting up a client is the authorization mechanism. The following authorization options are supported.
- Passing API Keys in the request header.
- Sending authorization tokens received from a client in the request header.
- Performing authorization on every request.
- Using a service to cache the authorization result and to monitor the token expiration time.
Let's consider each of these options.
API Key
If you have an on-premises installation, and you don't need to use any sophisticated authentication methods, you may use the API Key.
Tip
When a configuration is static (like a fixed API key), you may create an API Client once, as a singleton.
You may do so as follows:
Startup.cs (ConfigureServices
method):
services.AddSingleton<Aurigma.AssetStorage.IApiClientConfiguration>(s =>
{
return new Aurigma.AssetStorage.ApiClientConfiguration()
{
ApiUrl = "https://assetstorageapi.example.com/",
// API key specified in the service AppSettings.config
ApiKey = "YOUR_API_KEY",
};
});
Note
API Key is not available when you are using Customer's Canvas installed on our servers.
Token passthrough
This option will work for you if you already obtained an access token from our identity server (BackOffice). For example, if your application relies on the same identities that BackOffice does.
Let's imagine that we pass the token from the client to the server using the Authorization header. In this case, you may pass this token to Asset Storage (or other Back-end service) as follows.
Warning
Avoid using a singleton in this scenario - otherwise you will receive the token only on the first request after the application starts.
Startup.cs (ConfigureServices
method):
// It is required to get access to an HTTP header inside a configuration code
services.AddHttpContextAccessor();
services.AddScoped<Aurigma.AssetProcessor.IApiClientConfiguration>(s =>
{
var httpContextAccessor = s.GetRequiredService<IHttpContextAccessor>();
// Acquiring input token from request context
var token = httpContextAccessor?.HttpContext?.Request
.Headers["Authorization"].ToString().Replace("Bearer ", "");
return new Aurigma.AssetProcessor.ApiClientConfiguration()
{
// Service API url
ApiUrl = "https://assetstorageapi.example.com/",
AuthorizationToken = token
};
});
Get a token before each request
It is not always possible to get a token beforehand. In this case, you can have your application request a token from the BackOffice service before each request.
To do so, we will create a custom implementation of the API Client configuration class. It will acquire a token through the OAuth2 protocol and we will use IdentityModel package to achieve this. You can install it from Nuget like this:
Install-Package IdentityModel -Version 4.4.0
Now, create a new .cs file, for example, SampleAssetStorageApiClientConfiguration.cs and add the using
directive to be able to use the IdentityModel:
using IdentityModel.Client;
then add this class there:
public class SampleAssetStorageApiClientConfiguration :
Aurigma.AssetStorage.IApiClientConfiguration
{
private readonly HttpClient _client;
public SampleAssetStorageApiClientConfiguration(IHttpClientFactory clientFactory)
{
_client = clientFactory.CreateClient();
}
public async Task<string> GetAuthorizationTokenAsync()
{
var tokenResponse = await _client.RequestClientCredentialsTokenAsync(
new ClientCredentialsTokenRequest
{
// A token endpoint in the IdentityProvider (BackOffice token URL)
Address = "http://backoffice.example.com/connect/token",
// Client Id and Secret pair - you can register them in your
// BackOffice tenant control panel.
// NOTE: Be sure to provide all necessary scopes for this client!
ClientId = "YOUR_CLIENT_ID",
ClientSecret = "YOUR_CLIENT_SECRET"
});
if (tokenResponse.IsError)
{
throw new Exception("Could not retrieve token.");
}
return tokenResponse.AccessToken;
}
public string GetApiKey()
{
return "";
}
public string GetApiUrl()
{
return "https://assetstorageapi.example.com/";
}
}
Use it in the Startup.cs as follows:
services
.AddScoped<Aurigma.AssetStorage.IApiClientConfiguration, SampleAssetStorageApiClientConfiguration>();
Efficient way to get tokens
The method explained in the previous section works great if the amount of requests to Back-end services is low. However, acquiring a token per each request is not very efficient. You may want to cache a token and send a new authorization request only when it expires.
To do so, we need to implement a class that is responsible for receiving the token from BackOffice (a token service). Create a new .cs file, for example, TokenService.cs:
using IdentityModel.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
namespace SampleDotNetCore
{
class TokenInfo
{
public string AccessToken { get; set; }
public DateTime ExpiryTime { get; set; }
}
public class TokenService
{
// TokenService will be used as a singleton, so we need to
// synchronize access to the token stored in the service.
private readonly object _lockObject = new object();
private readonly HttpClient _client;
private TokenInfo _tokenInfo;
public TokenService(IHttpClientFactory clientFactory)
{
_client = clientFactory.CreateClient();
}
public async Task<string> GetTokenAsync()
{
lock (_lockObject)
{
if (_tokenInfo != null && _tokenInfo.ExpiryTime >= DateTime.UtcNow)
{
return _tokenInfo.AccessToken;
}
_tokenInfo = null;
}
var tokenResponse =
await _client.RequestClientCredentialsTokenAsync(
new ClientCredentialsTokenRequest
{
// A token endpoint in the IdentityProvider (BackOffice token URL)
Address = "http://backoffice.example.com/connect/token",
// Client Id and Secret pair - you can register them in your
// BackOffice tenant control panel.
// NOTE: Be sure to provide all necessary scopes for this client!
ClientId = "YOUR_CLIENT_ID",
ClientSecret = "YOUR_CLIENT_SECRET"
});
if (tokenResponse.IsError)
{
throw new Exception("Could not retrieve token.");
}
lock (_lockObject)
{
if (_tokenInfo == null)
{
_tokenInfo = new TokenInfo()
{
AccessToken = tokenResponse.AccessToken,
ExpiryTime = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn),
};
}
}
return tokenResponse.AccessToken;
}
}
}
Now, let's create a custom implementation of an API Client configuration class. This implementation will use the token service to request a token every time you initialize the client.
Create a new .cs file, for example, CustomAssetStorageApiClientConfiguration.cs:
public class CustomAssetStorageApiClientConfiguration :
Aurigma.AssetStorage.ApiClientConfiguration
{
public Func<Task<string>> TokenProvider;
public override Task<string> GetAuthorizationTokenAsync()
{
return TokenProvider();
}
}
Use it in the Startup.cs:
// Add a token service that is used to get tokens from BackOffice
services.AddSingleton<TokenService>();
services.AddScoped<Aurigma.AssetStorage.IApiClientConfiguration>(s =>
{
// Receive an instance of the TokenService we have just registered above.
var tokenService = s.GetRequiredService<TokenService>();
return new CustomAssetStorageApiClientConfiguration()
{
ApiUrl = "https://assetstorageapi.example.com/",
// TokenProvider is a function that is called every time you need a token.
// Let's just use our TokenService to receive the token.
TokenProvider = () => tokenService.GetTokenAsync()
};
});
In the API Reference, you can find details about the Asset Storage API, Asset Processor API, and Design Atoms API.