web.rest.client.overview - grecosoft/NetFusion GitHub Wiki
REST/HAL Client - Overview
This document describes how .NET clients can invoke REST/HAL based API services. A .NET client will most often be one of the following:
- ASP.NET Core WebApi host needing to call services exposed by another WebApi host.
- Console application needing to call services exposed by a WebApi host.
- .NET based mobile client.
The client assumes that the API host providing the services is based on the NetFusion.Rest.Server plug-in. The following NuGet packages are used by a .NET client:
- NetFusion.Rest.Common
- NetFusion.Rest.Client
Invoking API Services
The following is an example of the client-side resource that will be populated from the results of the service call:
using NetFusion.Rest.Client.Resources;
using System;
namespace Agent.Infrastructure.Adapters
{
public class ListingResource : HalResource
{
public int ListingId { get; set; }
public DateTime DateListed { get; set; }
public int NumberBeds { get; set; }
public int NumberFullBaths { get; set; }
public int NumberHalfBaths { get; set; }
public int SquareFeet { get; set; }
public decimal AcresLot { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
public decimal ListPrice { get; set; }
public decimal PricePerSqFt { get; set; }
public int YearBuild { get; set; }
}
}
Next, implement a method within the adapter as follows:
IRequestClient
All server API calls are executed by passing an instance of the APIRequest class to the IRequestClient send method. Some of the more common properties and methods used to create an ApiRequest instance will be described.
ApiRequest
Contains information about the request to be sent to the server. This class contains several factory methods used to create instances of this class. There are factory methods defined for the common HTTP methods: Get, Post, Put, and Delete. Also, instances can be created from Link objects associated with server-side returned resources. An example of creating an ApiRequest from a resource Link instance will be shown below.
RequestSettings
When calling a server API, request-settings specific to that request, can be specified. Default settings such as headers, accept types, and content type can be specified when registering the IRequestClient. When settings are specified during registration, any request specific settings are merged into the default settings. If the same setting is declared at the default and request levels, the request specific value is used. The RequestSettings class contains two child objects used for declaring headers and query parameters.
A RequestSettings instance is created using a factory method as follows:
using NetFusion.Rest.Client;
using NetFusion.Rest.Client.Settings;
using NetFusion.Rest.Config;
using System.Threading.Tasks;
namespace Agent.Infrastructure.Adapters
{
public class ListingAdapter : IListingAdapter
{
private readonly IRequestClient _requestClient;
public ListingAdapter(IRequestClientFactory requestClientFactory)
{
_requestClient = requestClientFactory.GetClient("ListingApi");
}
public async Task<ListingResource> GetListing(int listingId)
{
var settings = RequestSettings.Create(config => {
config.Headers.Add("max-count", "1000");
config.QueryString.AddParam("profile-name", "default");
});
var request = ApiRequest.Get("api/listing/{id}")
.WithRouteValues(t => t.id = listingId)
.UsingSettings(settings);
var response = await _requestClient.Send<ListingResource>(request);
return response.Content;
}
}
}
Resource Navigation
Once a resource is loaded, the associated link relations can be used to navigate to associated resources. This is accomplished by referencing the link to be navigated and creating an ApiRequest to be submitted. A ApiRequest can be created from a Link by invoking the ToRequest extension method. The following shows an example of a resource's link being navigated:
public async Task<PriceHistoryResource[]> GetPriceHistory(ListingResource listing)
{
var priceLink = listing.Links["listing:price-history"].ToRequest();
var response = await _requestClient.Send<PricingEventsResource>(priceLink);
var pricingEvents = response.Content;
return pricingEvents.GetEmbeddedCollection<PriceHistoryResource>("events").ToArray();
}
Embedded Resources
The server API can return embedded resources as defined by the HAL specification. The following is an example of server side code returning an embedded resource:
[NamedResource("user-comment")]
public class CommentResource : HalResource
{
public string Message { get; set; }
public string UserName { get; set; }
}
[HttpGet("{id}")]
public async Task<ListingResource> GetListing(int id)
{
var listing = await _listingRepo.GetListing(id);
var listingResource = _objectMapper.Map<ListingResource>(listing);
var comment = new CommentResource {
Message = "Sample embedded resource.",
UserName = "Sam Smith"
};
listingResource.Embed(comment);
return listingResource;
}
See the server REST/HAL documentation for more information about server-side embedded resources.
The following is the client-side code used to obtain the embedded resource. A client-side resource is created to model the embedded resource:
using NetFusion.Rest.Client.Resources;
namespace Agent.Infrastructure.Adapters
{
public class CommentResource : HalResource
{
public string Message { get; set; }
public string UserName { get; set; }
}
}
Next, the GetEmbedded method on the parent resource, returned from the server, is called passing the key identifying the name of the embedded resource assigned by the server:
var listing = await listingAdapter.GetListing(1);
var comment = listing.GetEmbedded<CommentResource>("user-comment");
When executing an API server call, the client can specify the embedded resources to be returned as follows:
using NetFusion.Rest.Client;
using NetFusion.Rest.Config;
using System.Threading.Tasks;
namespace Agent.Infrastructure.Adapters
{
public class ListingAdapter : IListingAdapter
{
private readonly IRequestClient _requestClient;
public ListingAdapter(IRequestClientFactory requestClientFactory)
{
_requestClient = requestClientFactory.GetClient("ListingApi");
}
public async Task<ListingResource> GetListing(int listingId)
{
var request = ApiRequest.Get("api/listing/{id}")
.WithRouteValues(t => t.id = listingId)
.Embed("user-comment");
var response = await _requestClient.Send<ListingResource>(request);
return response.Content;
}
}
}