Skip to main content

C# API Client

Customer's Canvas provides a powerful REST API for managing designs, assets, and backend services. Each service has its own package in Nuget. This tutorial demonstrates how to use the official C# API Client to interact with the API. We'll create a simple controller to fetch 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.

1. Create a project

  1. In Visual Studio, create a new project, either ASP.NET Web Application (.NET Framework) or ASP.NET Core Web Application.
  2. When creating the project, use the Web API project template.
  3. Ensure 64-bit IIS Express is enabled:
    • 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.

2. Install NuGet packages

For the purposes of this 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 the full list of available packages.

3. Set Up API Clients

The C# API Client uses dependency injection (DI) for configuration. We support both .NET Core and .NET Framework (with 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:

ConfigureServices example
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
ConfigureSwagger(services);

services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "http://localhost:21021";

options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};

options.RequireHttpsMetadata = false;
});

services.AddSingleton<IAuthorizationHandler, IdentityAuthenticatedUserAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, ApiKeyAuthenticatedUserAuthorizationHandler>();
services.AddAuthorization(options =>
{
options.AddPolicy("Custom",
policy => policy.Requirements.Add(new AuthenticatedUserAuthorizationRequirement()));
});

ConfigureDesignAtomsApiClients(services);
ConfigureAssetProcessorApiClients(services);
ConfigureAssetStorageApiClients(services);
//ConfigureAssetStorageApiClientsWithTokenService(services);

services.AddControllers();
}

Then add the configuration code like this:

Sample configuration code
private static void ConfigureAssetStorageApiClientsBase(IServiceCollection services)
{
// Registering custom configuration for API clients
// Setup clients configuration in DI callback

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>();
}

Now, let's see how you can create and use instances of API clients in your controllers and services.

4. Use the API Client

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 clients topic. 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:

API client usage simple example
[Authorize("Custom")]
[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.

API client usage example - override settings
[Authorize("Custom")]
[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);
}
}

5. Authentication

To make successful API calls, it is necessary to authorize them. The API client supports multiple authentication methods. For details, see the Authentication Guide.

The main thing you configure when setting up a client is authentication:

  1. Passing API Keys in the request header.
  2. Sending authorization tokens received from a client in the request header.
  3. Performing authorization on every request.
  4. Using a service to cache the authorization result and to monitor the token expiration time.

Let's consider each of these methods.

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):

Setting up API Key
private static void ConfigureAssetStorageApiClientsApiKey(IServiceCollection services)
{
// Registering custom configuration for API clients
// Setup clients configuration in DI callback

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",
};
});

// Using HttpClientFactory for managing HttpClient lifetime
services.AddHttpClient<IDesignAtomsServiceApiClient, DesignAtomsServiceApiClient>();
}
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):

Token passthrough
private static void ConfigureAssetStorageApiClientsToken(IServiceCollection services)
{
// 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
};
});

// Using HttpClientFactory for managing HttpClient lifetime
services.AddHttpClient<IDesignProcessorApiClient, DesignProcessorApiClient>();
services.AddHttpClient<IPrivateDesignProcessorApiClient, PrivateDesignProcessorApiClient>();
}

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
using IdentityModel.Client;

then add this class there:

SampleAssetStorageApiClientConfiguration class
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:

Automatically get token
private static void ConfigureAssetStorageApiClients(IServiceCollection services)
{
// Registering custom configuration for API clients
// Setup clients configuration in DI callback

services
.AddScoped<Aurigma.AssetStorage.IApiClientConfiguration, SampleAssetStorageApiClientConfiguration>();
// Using HttpClientFactory for managing HttpClient lifetime
services.AddHttpClient<IDesignsApiClient, DesignsApiClient>();
services.AddHttpClient<IPrivateDesignsApiClient, PrivateDesignsApiClient>();
}

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:

TokenService class implementation
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
{
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
{
Address = "http://backoffice.example.com/connect/token",
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:

CustomAssetStorageApiClientConfiguration class
public class CustomAssetStorageApiClientConfiguration :
Aurigma.AssetStorage.ApiClientConfiguration
{
public Func<Task<string>> TokenProvider;

public override Task<string> GetAuthorizationTokenAsync()
{
return TokenProvider();
}
}

Use it in the Startup.cs:

Automatically get token
private static void ConfigureAssetStorageApiClientsWithTokenService(IServiceCollection services)
{
services.AddHttpClient();

// 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()
};
});
// Using HttpClientFactory for managing HttpClient lifetime
services.AddHttpClient<IDesignsApiClient, DesignsApiClient>();
services.AddHttpClient<IPrivateDesignsApiClient, PrivateDesignsApiClient>();
}

6. Next Steps

Need Help?

Submit a support ticket

Was this page helpful?