Call Controller Actions using HttpClient
A few months ago I wrote
Use ASP.NET MVC Controller as API wherein I discussed how MVC controller can
be treated as a service. In that example the client used jQuery Ajax to call the
controller actions. Some readers asked whether such actions can be called by
HttpClient instead of jQuery. To that end this article explains how that can be
donw.
The HttpClient component is typically used to call a Web
API. The overall design of HttpClient component is geared towards REST services.
Its methods such as GetAsync(), PostAsJsonAsync(), PutAsJsonAsync()
and DeleteAsync() use the GET, POST, PUT and
DELETE verbs to invoke the respective Web API actions. However,
you can also use the HttpClient to call ordinary MVC actions. Of course, the
actions to be called must follow the pattern as expected by the HttpClient.
This article shows how HttpClient can be used to invoke MVC actions. We will
use the same CustomerController and Customer model explained
at the above link. So,
make sure you have the code ready before you go any further.
Since we wish to call the actions of CustomerController through HttpClient we
need to add a reference to System.Net.Http assembly. This assembly provides the
HttpClient component. Once referenced add HomeController to the Controllers
folder. The HomeController will have six action methods:
- Index()
- Insert()
- Insert(obj)
- Update(id)
- Update(obj)
- Delete(id)
The Index() action will invoke the GetAll() action on the CustomerController
to retrieve a List of Customer objects. The Insert() method will simply display
a blank view to accept new Customer details. The Insert(obj) method calls the
Post() action of the CustomerController and inserts the new customer into the
database. The Update(id) method retrieves a Customer whose CustomerID is
specified by calling GetByID() action of CustomerController. It then displays
the existing Customer details for editing. The Update(obj) action calls the
Put() action of the CustomerController and saves the modified Customer to the
database. The Delete() action calls the Delete() action of the
CustomerController and deletes a Customer.
The complete code of HomeController is given below:
public class HomeController : Controller
{
private HttpClient client = new HttpClient();
public HomeController()
{
client.BaseAddress = new Uri("http://localhost:49277");
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
}
public ActionResult Index()
{
HttpResponseMessage response = client.
GetAsync("/Customer/GetAll").Result;
List<Customer> data = response.Content.
ReadAsAsync<List<Customer>>().Result;
return View(data);
}
public ActionResult Update(string id)
{
HttpResponseMessage response = client.
GetAsync("/Customer/GetById" + $"/{id}").Result;
Customer data = response.Content.
ReadAsAsync<Customer>().Result;
return View(data);
}
[HttpPost]
public ActionResult Update(Customer obj)
{
if (ModelState.IsValid)
{
HttpResponseMessage response = client.
PutAsJsonAsync<Customer>("/Customer/Put" +
$"/{obj.CustomerID}", obj).Result;
TempData["Message"] = "Customer modified successfully!";
return RedirectToAction("Index");
}
else
{
return View(obj);
}
}
public ActionResult Insert()
{
return View();
}
[HttpPost]
public ActionResult Insert(Customer obj)
{
if (ModelState.IsValid)
{
HttpResponseMessage response = client.
PostAsJsonAsync<Customer>
("/Customer/Post", obj).Result;
TempData["Message"] = "Customer added successfully!";
return RedirectToAction("Index");
}
else
{
return View(obj);
}
}
public ActionResult Delete(string id)
{
HttpResponseMessage response = client.
DeleteAsync("/Customer/Delete" + $"/{id}").Result;
TempData["Message"] = "Customer deleted successfully!";
return RedirectToAction("Index");
}
}
When you call a Web API using HttpClient the URL is a REST based end point of
the Web API (for example, http://localhost/api/customer). However, you can't use
this pattern with the normal MVC controllers. If you observe the URLs in the
above code you will find that they point to the actual action such as GetAll and
Post. But apart from this difference the rest of the code is quite similar to
the Web API based application.
You will also need to add three views - Index, Insert and Update. These views
are given below:
Index view
@model List<MVCControllerViaHttpClient.Models.Customer>
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
</head>
<body>
<h1>List of Customers</h1>
<a href="/home/insert">Add New Customer</a>
<br />
@TempData["Message"]
<br />
<table border="1" cellpadding="10">
@foreach (var item in Model)
{
<tr>
<td>@item.CustomerID</td>
<td>@item.CompanyName</td>
<td><a href="/home/update/
@item.CustomerID">Edit</a></td>
<td><a href="/home/delete/
@item.CustomerID">Delete</a></td>
</tr>
}
</table>
</body>
</html>
Insert view
@model MVCControllerViaHttpClient.Models.Customer
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>New</title>
</head>
<body>
<h1>Add New Customer</h1>
@using (Html.BeginForm("insert", "home", FormMethod.Post))
{
<table border="1" cellpadding="10">
<tr>
<td>Customer ID :</td>
<td>@Html.TextBoxFor(m => m.CustomerID)</td>
</tr>
<tr>
<td>Company Name :</td>
<td>@Html.TextBoxFor(m => m.CompanyName)</td>
</tr>
<tr>
<td>Contact Name :</td>
<td>@Html.TextBoxFor(m => m.ContactName)</td>
</tr>
<tr>
<td>Country :</td>
<td>@Html.TextBoxFor(m => m.Country)</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Insert" />
</td>
</tr>
</table>
}
@ViewBag.Message
</body>
</html>
Update view
@model MVCControllerViaHttpClient.Models.Customer
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Edit</title>
</head>
<body>
<h1>Edit Existing Customer</h1>
@using (Html.BeginForm("update", "home", FormMethod.Post))
{
<table border="1" cellpadding="10">
<tr>
<td>Customer ID :</td>
<td>@Html.TextBoxFor(m => m.CustomerID,
new { @readonly="readonly" })</td>
</tr>
<tr>
<td>Company Name :</td>
<td>@Html.TextBoxFor(m => m.CompanyName)</td>
</tr>
<tr>
<td>Contact Name :</td>
<td>@Html.TextBoxFor(m => m.ContactName)</td>
</tr>
<tr>
<td>Country :</td>
<td>@Html.TextBoxFor(m => m.Country)</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Update" />
</td>
</tr>
</table>
}
@ViewBag.Message
</body>
</html>
Make sure to change the model namespace in the @model directive of all the
views as per your setup.
That's it! Run the Index view and test the insert, update and delete
operations.