Implementing the catalog HTTP client 

It is common practice to implement client libraries along with web services. Furthermore, it is the responsibility of a web service to provide a way to communicate with it. For that reason, we can represent the client's implementation using the following schema:

This allows us to publish the catalog service client library in an internal NuGet repository so that we can spread the client to other services. Moreover, the team that owns a specific service should know how it can be implemented and how to expose information in the right way. Let's start by creating two new classlib projects in the Catalog.API solution, which can be found in the src folder:

dotnet new classlib -n Catalog.API.Client -f netstandard2.1
dotnet sln ../Catalog.API.sln add Catalog.API.Client

dotnet new classlib -n Catalog.API.Contract -f netstandard2.1
dotnet sln ../Catalog.API.sln add Catalog.API.Contract

dotnet add Catalog.API.Client reference Catalog.API.Contract

The Catalog.API.Client project will contain all the methods we need to query the catalog service. Catalog.API.Contract includes the requests and responses that are used by the client to transfer the data, so we can proceed by copying the classes contained in the Responses folder of the Catalog.Domain project into the Catalog.API.Contract project we created previously. The resulting folder structure will look as follows:

.
├── Item
│ ├── ArtistResponse.cs
│ ├── GenreResponse.cs
│ ├── ItemResponse.cs
│ └── PriceResponse.cs
├── Catalog.API.Contract.csproj
├── bin
└── obj

In order to use the response models in a project, it is necessary to refer to Catalog.API.Contract. This practice is usually applied to the request and response classes. By doing this, it is possible to keep the contract of the API in a separate, continuous integration pipeline. As a second step, we need to create a new base client in the Catalog.API.Client project. The following IBaseClient interface defines the methods that are exposed by the client:

// /Base/IBaseClient.cs

using
System;
using System.Threading;
using System.Threading.Tasks;

namespace Catalog.API.Client.Base
{
public interface IBaseClient
{
Task<T> GetAsync<T>(Uri uri, CancellationToken
cancellationToken);
Uri BuildUri(string format);
}
}

The IBaseClient interface establishes the interface of the client. It exposes two main methods: GetAsync and BuildUri. Both of these methods are implemented in the BaseClient concrete class. The BaseClient class depends on the HttpClient and the string Url of our APIs. The GetAsync method calls the HttpClient and uses the Newtonsoft.Json package to deserialize the response of the client in a generic model, T.

Let's continue by defining the ICatalogItemResource interface and the CatalogItemResource classes. These classes represent the Item resource:

// Resources/ICatalogItemResource.cs

using
System;
using System.Threading;
using System.Threading.Tasks;
using Catalog.Contract.Item;

namespace Catalog.API.Client.Resources
{
public interface ICatalogItemResource
{
Task<ItemResponse> Get(Guid id, CancellationToken
cancellationToken = default);
}
}

ICatalogItemResource exposes the Get method by accepting the id and cancellationToken. It returns a Task<ItemResponse> type. Therefore, the CatalogItemResource model is defined in the Catalog.API.Client.Resources project as follows:

// Resources/CatalogItemResource.cs

using System;
using System.Threading;
using System.Threading.Tasks;
using Catalog.API.Client.Base;
using Catalog.Domain.Responses;

namespace Catalog.API.Client.Resources
{
public class CatalogItemResource : ICatalogItemResource
{
private readonly IBaseClient _client;

public CatalogItemResource(IBaseClient client)
{
_client = client;
}

private Uri BuildUri(Guid id, string path = "")
{
return _client.BuildUri(string.Format("api/items/{0}", id,
path));

}

public async Task<ItemResponse> Get(Guid id, CancellationToken
cancellationToken)
{
var uri = BuildUri(id);
return await _client.GetAsync<ItemResponse>(uri,
cancellationToken);

}
}
}

CatalogItemResource refers to the IBaseClient interface and it implements the Get method by using the IBaseClient interface. In the same way, CatalogItemResource is also in charge of providing the paths of the items' resources by building the Uri of the web service. Besides this, CatalogItemResource uses the IBaseClient wrapper to perform HTTP operations. Let's dig into the implementation of the IBaseClass interface:

// /Base/BaseClient.cs

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace Catalog.API.Client.Base
{
public class BaseClient : IBaseClient
{
private readonly HttpClient _client;
private readonly string _baseUri;

public BaseClient(HttpClient client, string baseUri)
{
_client = client;
_baseUri = baseUri;
}

public async Task<T> GetAsync<T>(Uri uri, CancellationToken
cancellationToken)
{
var result = await _client.GetAsync(uri,
cancellationToken);
result.EnsureSuccessStatusCode();

return JsonConvert.DeserializeObject<T>(await
result.Content.ReadAsStringAsync());
}

public Uri BuildUri(string format)
{
return new UriBuilder(_baseUri)
{
Path = format
}.Uri;
}
}
}

The preceding code uses the HttpClient class that's provided by the framework to implement the GetAsync<T> generic method. Therefore, using this generic pattern allows us to deserialize the response using a custom model.

Finally, we can implement the actual client of the service by adding the following components:

// ICatalogClient.cs

using
Catalog.API.Client.Resources;

namespace Catalog.API.Client
{
public interface ICatalogClient
{
ICatalogItemResource Item { get; }
}
}

// CatalogClient.cs

using System.Net.Http;
using Catalog.API.Client.Base;
using Catalog.API.Client.Resources;

namespace Catalog.API.Client
{
public class CatalogClient : ICatalogClient
{
public ICatalogItemResource Item { get; }

public CatalogClient(HttpClient client)
{
Item = new CatalogItemResource(new BaseClient(client,
client.BaseAddress.ToString()));
}
}
}

Finally, it is possible to use Catalog.API.Client to instantiate a new HTTP client instance and call the catalog service using a unique and universal contract:

var catalogClient = new CatalogClient(new HttpClient());
var result = await catalogClient.Item.Get(new Guid(item.CartItemId), cancellationToken);

Now, we have some standalone DLLs that provide everything we need, so that we can query the catalog web service. In the next section, we will learn how to perform HTTP calls to the catalog service using the client we implemented in this section.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.84.155