Back to Website
Show / Hide Table of Contents

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

  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. 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.

  • .NET Core
  • .NET Framework

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

If you did not do it earlier, install the Autofac module from Nuget:

Install-Package Autofac -Version 6.0.0

Then add these using to the global.asax.cs:

using Autofac;
using Autofac.Integration.WebApi;

Then add this to the Application_Start method:

protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);
    RouteConfig.RegisterRoutes(RouteTable.Routes);

    var builder = new ContainerBuilder();

    builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

    // Here we will set up API clients
    ConfigureAssetStorageApiClients(builder);

    var container = builder.Build();

    GlobalConfiguration.Configuration.DependencyResolver = 
        new AutofacWebApiDependencyResolver(container);
}

Now let's implement the ConfigureAssetStorageApiClients where we will configure the API client.

private void ConfigureAssetStorageApiClients(ContainerBuilder builder)
{
    // Use single http client for all clients
    builder.RegisterType<HttpClient>()
        .AsSelf()
        .SingleInstance();

    // Registering custom configuration for every API client
    builder.RegisterType<Aurigma.AssetStorage.ApiClientConfiguration>()
        .As<Aurigma.AssetStorage.IApiClientConfiguration>()
        .InstancePerRequest()
        // Setup clients configuration in DI callback
        .OnActivating(e => 
        {
            // AssetStorage service API url
            e.Instance.ApiUrl = "https://assetstorageapi.example.com/";
            // Auth parameters 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.
    builder.RegisterType<DesignsApiClient>()
        .As<IDesignsApiClient>();
    builder.RegisterType<PrivateDesignsApiClient>()
        .As<IPrivateDesignsApiClient>();
}
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:

  • .NET Core
  • .NET Framework
[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);
    }
}
public class SampleController : ApiController
{
    private readonly IDesignsApiClient _client;

    public SampleController(IDesignsApiClient client)
    {
        _client = client;
    }

    [HttpGet]
    [Route("action")]
    public async Task<IHttpActionResult> 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.

  • .NET Core
  • .NET Framework
[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);
    }
}
public class Sample2Controller : ApiController
{
    private readonly IDesignsApiClient _client;
    private readonly IPrivateDesignsApiClient _privateClient;

    public Sample2Controller(IDesignsApiClient client, IPrivateDesignsApiClient privateClient)
    {
        _client = client;
        _privateClient = privateClient;

        // we can override client settings 
        var token = HttpContext.Current?.Request?.Headers["Authorization"]?
            .ToString().Replace("Bearer ", "");
        _client.AuthorizationToken = token;
        _privateClient.AuthorizationToken = token;
    }

    [HttpGet]
    [Route("action")]
    public async Task<IHttpActionResult> 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.

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

  • .NET Core
  • .NET Framework

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

Global.asax.cs:

// Registering custom configuration for API clients
builder.RegisterType<Aurigma.AssetProcessor.ApiClientConfiguration>()
        .As<Aurigma.AssetStorage.IApiClientConfiguration>()
        .SingleInstance()
        // Setup clients configuration in DI callback
        .OnActivating(e =>                                                        
        {
            e.Instance.ApiUrl = "https://assetstorageapi.example.com/";                        
            // API key specified in the service AppSettings.config
            e.Instance.ApiKey = "ApiKey";
        });
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.

  • .NET Core
  • .NET Framework

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

Global.asax.cs:

// Registering custom configuration for API clients
builder.RegisterType<Aurigma.AssetProcessor.ApiClientConfiguration>()
    .As<Aurigma.AssetStorage.IApiClientConfiguration>()
    .InstancePerRequest()
    // Setup clients configuration in DI callback
    .OnActivating(e =>
    {
        // Acquiring input token from request context
        var token = HttpContext.Current?.Request?.Headers["Authorization"]?
            .ToString().Replace("Bearer ", "");

        e.Instance.ApiUrl = "https://assetstorageapi.example.com/";
        // Setup Token in DI
        e.Instance.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:

  • .NET Core
  • .NET Framework
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>();
public class SampleAssetStorageApiClientConfiguration : 
    Aurigma.AssetStorage.IApiClientConfiguration
{
    private readonly HttpClient _client;
    public SampleAssetStorageApiClientConfiguration(HttpClient client)
    {
        _client = client;
    }

    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 HttpException((int)HttpStatusCode.InternalServerError, 
                "Could not retrieve token.");
        }

        return tokenResponse.AccessToken;
    }

    public string GetApiKey()
    {
        return "";
    }

    public string GetApiUrl()
    {
        return "https://assetstorageapi.example.com/";
    }
}

Use it in the Global.asax.cs as follows:

private void ConfigureAssetStorageApiClientsSampleConfig(ContainerBuilder builder)
{
    builder.RegisterType<HttpClient>()
        .AsSelf()
        .SingleInstance();

    // Note, the setup callback is no longer needed
    builder.RegisterType<SampleAssetStorageApiClientConfiguration>()
        .As<Aurigma.AssetStorage.IApiClientConfiguration>()
        .InstancePerRequest();

    builder.RegisterType<DesignsApiClient>()
        .As<IDesignsApiClient>();
    builder.RegisterType<PrivateDesignsApiClient>()
        .As<IPrivateDesignsApiClient>();
}

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:

  • .NET Core
  • .NET Framework
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;
        }
    }
}
using IdentityModel.Client;
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web;

namespace SampleDotNetWebApi2
{
    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(HttpClient client)
        {
            _client = client;
        }

        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 HttpException((int)HttpStatusCode.InternalServerError, 
                    "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:

  • .NET Core
  • .NET Framework
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()
    };
});
public class CustomAssetStorageApiClientConfiguration : 
    Aurigma.AssetStorage.ApiClientConfiguration
{
    public Func<Task<string>> TokenProvider;
    
    public override Task<string> GetAuthorizationTokenAsync()
    {
        return TokenProvider();
    }
}

Use it in the Global.asax.cs:

private void ConfigureAssetStorageApiClientsWithTokenService(ContainerBuilder builder)
{
    builder.RegisterType<HttpClient>()
        .AsSelf()
        .SingleInstance();

    // Add a token service that is used to get tokens from BackOffice 
    builder.RegisterType<TokenService>()                                             
        .AsSelf()
        .SingleInstance();

    builder.RegisterType<CustomAssetStorageApiClientConfiguration>()
        .As<Aurigma.AssetStorage.IApiClientConfiguration>()
        .InstancePerRequest()
        .OnActivating(e =>
        {
            // Receive an instance of the TokenService we have just registered above.
            var tokenService = e.Context.Resolve<TokenService>();

            e.Instance.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.
            e.Instance.TokenProvider = () => tokenService.GetTokenAsync();
        });

    builder.RegisterType<DesignsApiClient>()
        .As<IDesignsApiClient>();
}

In the API Reference, you can find details about the Asset Storage API, Asset Processor API, and Design Atoms API.

Was this page helpful?
Thanks for your feedback!
Back to top Copyright © 2001–2024 Aurigma, Inc. All rights reserved.
Loading...
    Thank for your vote
    Your opinion is important to us. To provide details, send feedback.
    Send feedback