Show / Hide Table of Contents

Using API in C# application

In this tutorial, we will show you how you can use Customer's Canvas REST API with a help of official C# API Client. As an example, we will create a simple controller which requests a list of design folders.

It is supposed that you already have an instance of Customer's Canvas set up and running on our infrastructure or your servers.

Preparing

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:
    • On 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 purpose 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

Setting Up the Application

The Back-end Services allow you to implement the following authentication approaches:

  1. Passing Api Keys in the request header.
  2. Passing tokens 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.

Now, let's set up the authentication, depending on your framework:

  • .NET Framework
  • ASP.NET Core

.Net Framework

In our ASP.NET Framework project, we will use Autofac Inversion Of Control container. You can also use other IoC containers instead. In this project, you need to change Application_Start in the Global.asax.cs file. At the end of this method, add:

var builder = new ContainerBuilder();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

builder.RegisterType<HttpClient>()
	.AsSelf()
	.SingleInstance();

/*
	Configure API client authentication here.
*/

var container = builder.Build();
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);

Let's learn how you can configure API clients based on different authentication approaches.

1. Configuring a Client with API Keys

For example, define an API key for Aurigma.DesignAtomsApi.ApiClient.

// Register custom configuration for API clients.
builder.RegisterType<Aurigma.DesignAtomsApi.ApiClientConfiguration>()
	.As<Aurigma.DesignAtomsApi.IApiClientConfiguration>()
	.InstancePerRequest()
	.OnActivating(e => // Set up client configuration in the DI callback.
	{
		// Specify a DesingAtomsApi service URL.
		e.Instance.ApiUrl = "http://localhost:56416/";
		// Set up a DesingAtomsApi API key.
		e.Instance.ApiKey = "ApiKey";
	});

// Register API clients.
builder.RegisterType<DesignAtomsServiceApiClient>()
	.As<IDesignAtomsServiceApiClient>();

2. Configuring a Client with Tokens in the Header

This authentication approach is well suited, for example, for communication between services, when AssetProcessor accesses AssetStorage. In the following example, we define a token for Aurigma.AssetProcessor.ApiClient.

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

		// Specify an AssetProcessor service URL.
		e.Instance.ApiUrl = "http://localhost:64288/";
		// Set up a token.
		e.Instance.AuthorizationToken = token;
	});

// Register API clients.
builder.RegisterType<DesignProcessorApiClient>()
	.As<IDesignProcessorApiClient>();
builder.RegisterType<PrivateDesignProcessorApiClient>()
	.As<IPrivateDesignProcessorApiClient>();

3. Configuring a Client with Authorization for a Request.

In this example, authorization is carried out in the SampleAssetStorageApiClientConfiguration.GetToken method, which is called with each request from a client that uses this configuration.

// Register custom configuration for API clients.
builder.RegisterType<SampleAssetStorageApiClientConfiguration>()
	.As<Aurigma.AssetStorage.IApiClientConfiguration>()
	.InstancePerRequest()
	.OnActivating(e => // Set up client configuration in the DI callback.
	{
		// Specify an AssetStorage service URL.
		e.Instance.ApiUrl = "http://localhost:56416/";
	});

// Register API clients.
builder.RegisterType<DesignsApiClient>()
	.As<IDesignsApiClient>();
builder.RegisterType<PrivateDesignsApiClient>()
	.As<IPrivateDesignsApiClient>();

SampleAssetStorageApiClientConfiguration implements the Aurigma.AssetStorage.IApiClientConfiguration interface and uses the IdentityModel client library.

using IdentityModel.Client;
 
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
		{
			// BackOffice Identity Server URL.
			Address = "http://localhost:21021/connect/token",
			ClientId = "<YOUR CLIENT ID>",
			ClientSecret = "<YOUR CLIENT SECRET>"
		});

		if (tokenResponse.IsError)
		{
			throw new HttpException((int)HttpStatusCode.InternalServerError, "Could not retrieve token.");
		}

		return tokenResponse.AccessToken;
	}
}

4. Configuring a Client with a Token Service.

This approach is an improved version of the previous one. We implemented CustomAssetStorageApiClientConfiguration (not included in the ApiClient packages) that allows for defining an arbitrary authorization function in the DI callback. The TokenService service is also a custom service(not included in the ApiClient packages), Which returns a cached token or makes an authorization request in the GetTokenAsync method.

// Register TokenService to handle acquiring a token.
builder.RegisterType<TokenService>()                                             
	.AsSelf()
	.SingleInstance();

// Register custom configuration for API clients.
builder.RegisterType<CustomAssetStorageApiClientConfiguration>()
	.As<Aurigma.AssetStorage.IApiClientConfiguration>()
	.InstancePerRequest()
	.OnActivating(e => // Set up client configuration in the DI callback.
	{
		var tokenService = e.Context.Resolve<TokenService>();

		// Specify an AssetStorage service URL.
		e.Instance.ApiUrl = "http://localhost:56416/";
		// Set up TokenProvider function using the TokenService.
		e.Instance.TokenProvider = () => tokenService.GetTokenAsync();
	});

// Register API clients.
builder.RegisterType<DesignsApiClient>()
	.As<IDesignsApiClient>();
builder.RegisterType<PrivateDesignsApiClient>()
	.As<IPrivateDesignsApiClient>();

ASP.NET Core

For an ASP.NET Core project, change the ConfigureServices method in Startup.cs. Before adding controllers, paste:

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

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

		options.RequireHttpsMetadata = false;
	});

/*
	Configure API client authentication here.
*/

Let's learn how you can configure API clients based on different authentication approaches.

1. Configuring a Client with API Keys

For example, define an API key for Aurigma.DesignAtomsApi.ApiClient.

// Register custom configuration for API clients.
// Set up client configuration in the DI callback.
services.AddScoped<Aurigma.DesignAtomsApi.IApiClientConfiguration>(s =>
{
	return new Aurigma.DesignAtomsApi.ApiClientConfiguration()
	{
		// Specify a DesingAtomsApi service URL.
		ApiUrl = "http://localhost:56416/";
		// Set up a DesingAtomsApi API key.
		ApiKey = "ApiKey"
	};
});

// Use HttpClientFactory for managing HttpClient lifetime.
services.AddHttpClient<IDesignAtomsServiceApiClient, DesignAtomsServiceApiClient>();

2. Configuring a Client with Tokens in the Header

In this example, we define a token for Aurigma.AssetProcessor.ApiClient.

services.AddHttpContextAccessor();

// Register custom configuration for API clients.
// Set up client configuration in the DI callback.
services.AddScoped<Aurigma.AssetProcessor.IApiClientConfiguration>(s =>
{
	var httpContextAccessor = s.GetRequiredService<IHttpContextAccessor>();

	// Acquire an input token from the request context.
	var token = httpContextAccessor?.HttpContext?.Request
		.Headers["Authorization"].ToString().Replace("Bearer ", "");

	return new Aurigma.AssetProcessor.ApiClientConfiguration()
	{
		// Specify an AssetProcessor service URL.
		ApiUrl = "http://localhost:64288/",
		// Set up a token.
		AuthorizationToken = token
	};
});

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

3. Configuring a Client with Authorization for a Request.

In this example, authorization is carried out in the SampleAssetStorageApiClientConfiguration.GetToken method, which is called with each request from a client that uses this configuration. SampleAssetStorageApiClientConfiguration implements the Aurigma.AssetStorage.IApiClientConfiguration interface.

// Set up client configuration in the DI callback.
services.AddScoped<Aurigma.AssetStorage.IApiClientConfiguration>(s =>
{
	var httpClientFactory = s.GetRequiredService<IHttpClientFactory>();

	// Register custom configuration for API clients.
	return new SampleAssetStorageApiClientConfiguration(httpClientFactory)
	{
		// Specify an AssetStorage service URL.
		ApiUrl = "http://localhost:56416/"
	};
});

// Use HttpClientFactory for managing HttpClient lifetime.
services.AddHttpClient<IDesignsApiClient, DesignsApiClient>();
services.AddHttpClient<IPrivateDesignsApiClient, PrivateDesignsApiClient>();

4. Configuring a Client with a Token Service.

This approach is an improved version of the previous one. We implemented CustomAssetStorageApiClientConfiguration (not included in the ApiClient packages) that allows for defining an arbitrary authorization function in the DI callback. The TokenService service is also a custom service(not included in the ApiClient packages), Which returns a cached token or makes an authorization request in the GetTokenAsync method.

services.AddHttpClient();
services.AddSingleton<TokenService>();

// Register custom configuration for API clients.
// Set up client configuration in the DI callback.
services.AddScoped<Aurigma.AssetStorage.IApiClientConfiguration>(s =>
{
	var tokenService = s.GetRequiredService<TokenService>();

	return new CustomAssetStorageApiClientConfiguration()
	{
		// Specify an AssetStorage service URL.
		ApiUrl = "http://localhost:56416/",
		// Set up TokenProvider function using the TokenService.
		TokenProvider = () => tokenService.GetTokenAsync()
	};
});

// Use HttpClientFactory for managing HttpClient lifetime.
services.AddHttpClient<IDesignsApiClient, DesignsApiClient>();
services.AddHttpClient<IPrivateDesignsApiClient, PrivateDesignsApiClient>();

Creating Custom Endpoints

In your client implementation, you can override endpoints of the Back-end services. For following example illustrates how you can use Aurigma.AssetStorage.IDesignsApiClient to retrieve a list of designs in a specific folder.

public class DefaultController : ControllerBase
{
	private readonly IDesignsApiClient _designsClient;	

	public DefaultController(IDesignsApiClient client)
	{
		_designsClient = client;
		
		// You can override client settings here if needed, for example:

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

	[HttpGet("action")]
	public async Task<ActionResult> Action()
	{
		FolderContentOfDesignDto folderContent = await _designsClient.GetFolderAsync();

		// Some actions here...

		return Ok();
	}
}

In Connecting the Design Editor topic, we will talk about how to connect the Design Editor to the Customer's Canvas Back-end Services.

In the API Reference, you can find details about the AssetStorage API, AssetProcessor API, and DesignAtoms API.

Back to top Aurigma, Inc. 2020