Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How Do You Post to a Web API Controller from an MVC Controller?

0.00/5 (No votes)
4 Jan 2016 1  
How do you post to a Web API controller from an MVC controller?

In this interconnected world, you have to deal with APIs all the time. A lot of the time, you’re just querying them. Retrieving data. Sometimes you’re calling a public API. Most of the time, you’re calling your own, internal API. If you’re calling your own API, it’s likely that you’ll need to post to it at some point, too. Perhaps you need to send images or documents to it. Or maybe it’s good old fashioned form data. Fire it across via AJAX, job done? That makes sense for an image or a document. But what about sending form data? Chances are your form data’s a bit different from the API data. Better handle that in C# then. But how?

HttpClient to the Rescue Again

In my last post, we looked at a way to call a Web API controller from C#. We’ll build upon that in this post. We’ll see how we can post form data across to an API. We’ll be using our old friend, the HttpClient.

If you want to follow along, you can grab the Visual Studio solution from the last article. If you'd rather just grab the finished code, you can get it from the link below.

View the original article

In this scenario, we’re calling a simple Web API Controller action. It just adds a new product via a Post request. We’re not interested in the details of the controller action in this post. Instead, we’ll be focusing on how we make that call. To do that, we’ll add a method to our ApiClient. This will handle posting JSON-encoded data to an API endpoint.

public interface IApiClient
{
    Task<HttpResponseMessage> 
    PostJsonEncodedContent<T>(string requestUri, T content) where T : ApiModel;
}

public class ApiClient : IApiClient
{
    private readonly HttpClient httpClient;
    private const string BaseUri = "Http://localhost:28601/";

    public ApiClient(HttpClient httpClient)
    {
        this.httpClient = httpClient;
    }

    public async Task<HttpResponseMessage> 
    PostJsonEncodedContent<T>(string requestUri, T content) where T : ApiModel
    {
        httpClient.BaseAddress = new Uri(BaseUri);
        httpClient.DefaultRequestHeaders.Accept.Clear();
        httpClient.DefaultRequestHeaders.Accept.Add
        (new MediaTypeWithQualityHeaderValue("application/json"));
        var response = await httpClient.PostAsJsonAsync(requestUri, content);
        return response;
    }
}

How Do We Call the API Client?

PostAsJsonAsync is a handy extension method from the Microsoft.AspNet.WebApi.Client Nuget package. It lives in the System.Net.Http.Formatting DLL. We also need to set the request header to accept JSON and we’re good to go. Now let’s take a look at the ProductClient:

public interface IProductClient
{
    Task<CreateProductResponse> CreateProduct(ProductViewModel product);
}

public class ProductClient : ClientBase, IProductClient
{
    private const string ProductsUri = "api/products";

    public ProductClient(IApiClient apiClient) : base(apiClient)
    {
    }

    public async Task<CreateProductResponse> CreateProduct(ProductViewModel product)
    {
        var apiModel = new ProductApiModel
        {
            CreatedOn = DateTime.Now,
            Name = product.Name,
            Description = product.Description
        };
        var createProductResponse = await PostEncodedContentWithSimpleResponse
        <CreateProductResponse, ProductApiModel>(ProductsUri, apiModel);
        return createProductResponse;
    }
}

public abstract class ClientBase
{
    private readonly IApiClient apiClient;

    protected ClientBase(IApiClient apiClient)
    {
        this.apiClient = apiClient;
    }

    protected async Task<TResponse> 
    PostEncodedContentWithSimpleResponse<TResponse, TModel>(string url, TModel model)
        where TModel : ApiModel
        where TResponse : ApiResponse<int>, new()
    {
        using (var apiResponse = await apiClient.PostJsonEncodedContent(url, model))
        {
            var response = await CreateJsonResponse<TResponse>(apiResponse);
            response.Data = Json.Decode<int>(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;
    }
}

Here we map a ViewModel to the API model we need to send to the Web API endpoint. I've kept it simple, but in a production system, I’d recommend using a mapper here. We then post the data across via the ApiClient. Our API post action will return the id of the product after creation. We grab that and set it as the Data property of our response. We can then use that id in our MVC ProductController. This assumes that you use integers for your ids. Maybe you use strings instead. It’s trivial to change ApiResponse<int> in PostEncodedContentWithSimpleResponse as you need to.

Last Step, the MVC Controller

Now that's all set up, we need to call it from an MVC Controller. We're making an asynchronous call, so our MVC Controller action needs to be asynchronous too.

public class ProductController : Controller
{
    private readonly IProductClient productClient;

    public ProductController()
    {
        var apiClient = new ApiClient();
        productClient = new ProductClient(apiClient);
    }

    public ProductController(IProductClient productClient)
    {
        this.productClient = productClient;
    }

    public ActionResult CreateProduct()
    {
        var model = new ProductViewModel();
        return View(model);
    }

    [HttpPost]
    public async Task<ActionResult> CreateProduct(ProductViewModel model)
    {
        var response = await productClient.CreateProduct(model);
        var productId = response.Data;
        return RedirectToAction("GetProduct", new {id = productId});
    }
}

All we're doing here is calling the ProductClient and then redirecting to the GetProduct action. We're not worrying about whether there were any errors in the API call. I'll be covering that in a future post. Neither are we focusing on displaying a message to the user after the operation completes. If we were, we could use TempData for that.

Wrapping Up

We covered a fair bit in this article. Let's recap:

  • We added a post method to the ApiClient to post JSON to an API endpoint
  • We wrapped that call in a ProductClient that called out to a specific endpoint to create a product
  • We decoded the integer id value returned from the API call and passed it back to the calling code
  • We hooked all of this up within an async Controller action in our MVC code

View the original article

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here