Ever had to call an API from your MVC app? A lot of the time, you can use JavaScript. But there are times when the client-side approach just doesn't cut it. Maybe your app needs to use the API as if it were a database, or you’re calling a public
API. Chances are you need to transform the data somehow before it gets anywhere near the browser. Looks like you’ll have to call the API from your C# code. Sounds simple enough, right? Trouble is, you can’t seem to find any examples showing how to do this. You know how to do it using jQuery via a simple AJAX call. Can you do it in C# though?
HttpClient to the Rescue
The good news is that you can and it’s quite straightforward. HttpClient
exists for just this kind of scenario. In this post, we’ll look at how to call a WebAPI Controller endpoint from MVC. By default, WebAPI Controller actions return JSON. So when you call them, you get back a big string
. You then parse this string
into an object inside your MVC application and you're good to go. Let’s see this in action.
View the original article
In our scenario, we've got a simple WebAPI Controller that returns a product by its id. The JSON returned looks something like this:
{"ProductId":3,"Name":"Product 3","Description":"Description 3",
"CreatedOn":"2015-11-13T17:01:18.6953449+00:00"}
Let's start by creating an ApiClient
that we can call to hit any API and return the result. We'll then create a specific product client that uses the ApiClient
. It will hit the product API controller and return a ProductApiModel
. Here’s the ApiClient
:
public interface IApiClient
{
Task<HttpResponseMessage> GetFormEncodedContent
(string requestUri, params KeyValuePair<string, string>[] values);
}
public class ApiClient : IApiClient
{
private const string BaseUri = "Http://localhost:28601/";
public async Task<HttpResponseMessage>
GetFormEncodedContent(string requestUri, params KeyValuePair<string, string>[] values)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(BaseUri);
using (var content = new FormUrlEncodedContent(values))
{
var query = await content.ReadAsStringAsync();
var requestUriWithQuery = string.Concat(requestUri, "?", query);
var response = await client.GetAsync(requestUriWithQuery);
return response;
}
}
}
}
Both FormEncodedContent
and HttpClient
expose async methods only, so the calls are both awaitable. First of all, we encode the parameters we’re passing across. We then retrieve the JSON data from the API as a string
.
Data Classes
Now it’s time to turn that into an ApiModel
object. Before we do that, let's have a look at the data classes we'll be using:
public abstract class ApiResponse
{
public bool StatusIsSuccessful { get; set; }
public HttpStatusCode ResponseCode { get; set; }
public string ResponseResult { get; set; }
}
public abstract class ApiResponse<T> : ApiResponse
{
public T Data { get; set; }
}
public class ProductApiModel
{
public int ProductId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime CreatedOn { get; set; }
}
public class ProductResponse : ApiResponse<ProductApiModel>
{
}
The Product Client
And now the product client itself. We're introducing a base class here too with methods for decoding the JSON. This also allows us to hide the ApiClient
from any calling code:
public interface IProductClient
{
Task<ProductResponse> GetProduct(int productId);
}
public class ProductClient : ClientBase, IProductClient
{
private const string ProductUri = "api/product";
public ProductClient(IApiClient apiClient) : base(apiClient)
{
}
public async Task<ProductResponse> GetProduct(int productId)
{
var idPair = new KeyValuePair<string, string>("id", productId.ToString());
return await GetJsonDecodedContent<ProductResponse, ProductApiModel>(ProductUri, idPair);
}
}
public abstract class ClientBase
{
private readonly IApiClient apiClient;
protected ClientBase(IApiClient apiClient)
{
this.apiClient = apiClient;
}
protected async Task<TResponse> GetJsonDecodedContent<TResponse, TContentResponse>
(string uri, params KeyValuePair<string, string>[] requestParameters)
where TResponse : ApiResponse<TContentResponse>, new()
{
var apiResponse = await apiClient.GetFormEncodedContent(uri, requestParameters);
var response = await CreateJsonResponse<TResponse>(apiResponse);
response.Data = Json.Decode<TContentResponse>(response.ResponseResult);
return response;
}
private static async Task<TResponse> CreateJsonResponse<TResponse>
(HttpResponseMessage response) where TResponse : ApiResponse, new()
{
var clientResponse = new TResponse
{
StatusIsSuccessful = response.IsSuccessStatusCode,
ResponseCode = response.StatusCode
};
if (response.Content != null)
{
clientResponse.ResponseResult = await response.Content.ReadAsStringAsync();
}
return clientResponse;
}
}
How Do We Call This?
The last thing to do is see how we could call this from an MVC controller (or anywhere else). Because our API call is asynchronous, the MVC Controller action must be asynchronous too.
public class ProductController : Controller
{
private readonly IProductClient productClient;
public HomeController(IProductClient productClient)
{
this.productClient = productClient;
}
public async Task<ActionResult> GetProduct(int id)
{
var product = await productClient.GetProduct(id);
var model = return View(model);
}
}
Wrapping Up
We covered a fair bit in this article. Let's recap:
- We created an
ApiClient
to call out to an API endpoint
- We wrapped that call in a
ProductClient
that called out to a specific endpoint to return a product
- We parsed the JSON
string
from the API into a ProductApiModel
within our domain
- We hooked all of this up within an async Controller action in our MVC code
View the original article