Bradley Braithwaite
1 import {
2 Learning,
3 JavaScript,
4 AngularJS
5 } from 'bradoncode.com'
6 |

Designing an MVC UI Layer for use with Web API : 10 Years of .Net Compressed into Weeks #16

on
on Design Patterns, asp.net 10 years on, ASP.Net MVC

This post looks at creating an ASP.Net MVC UI layer to consume a RESTful HTTP API. This post is part of a blog series ASP.Net 10 Years On. Even though this is part of a series I have tried to make each post standalone. Previously, we looked at Testing an ASP.Net Web API. Now that we've covered how to create and test the API let us now explore how to consume it.

Early on in the series it was defined that MVC 4.0 with a Razor view engine would be the technology of choice. It’s worthwhile to note the fact that the application is exposed via API over HTTP so we could use any form of technology for the UI such as Ruby, PHP JavaScript, iOS etc. We have total flexibility because we made the decision to replace the service layer with an HTTP based API.

Controllers and API Authentication

Let’s dive in and look at some code. The following snippet is from the UsersController of the MVC project. The UsersController simply offers CRUD functionality for managing users. Since we have zero business logic in the UI project and have to call a separate entity to call into this logic over HTTP we need to define a common interface for accessing the API:


[Authorize]
public class UsersController : BaseController
{
public UsersController(IBaseContext context) :base(context) { }

//
// GET: /Users
[HttpGet]
public ActionResult Index()
{
var response = Client.GetAsync("users").Result;

if (response.IsSuccessStatusCode)
{
return View(response.Content.ReadAsAsync<dynamic>().Result);
}

return ErrorView(response);
}
}

The notable line of code within this action is:


var response = Client.GetAsync("users").Result;

NB The “users” string is the name of the resource i.e. it translates to the url “http://localhost/api/users”.

The Client property from the BaseController class look as:


public HttpClient Client {
get
{
if (!string.IsNullOrEmpty(FormsAuthentication.GetAuthenticationToken()))
{
_client.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue(FormsAuthentication.GetAuthenticationToken());
}

return _client;
}
}

Note that in this method we are checking for the presence of an “authentication token”. If the user is authenticated, a token is returned from the API and retained on the client side to be sent with each request. Logic within the API can determine the rules to determine for how long such a token can remain valid etc.

NB The code to authenticate and retrieve the token can be found in the AuthenticationController.

The implementation of the client property is resolved via dependency injection. Here is the implementation:


public class ApiClient
{
public static HttpClient GetClient()
{
var client = new HttpClient()
{
BaseAddress = new Uri(ConfigurationManager.AppSettings["BaseApiUri"]),
};

client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

return client;
}
}

Here is how the action method to create a user would look:


//
// GET: /Users/Create
[HttpPost]
public ActionResult Create(UserView user)
{
var response = Client.PostAsJsonAsync("users", user).Result;

switch (response.StatusCode)
{
case HttpStatusCode.Created:
return RedirectToAction("Index");
case HttpStatusCode.BadRequest:
AddModelErrors(response);
return View(user);
}

return ErrorView(response);
}

And the action for updating a user:


//
// POST: /Users/Edit/B5608F8E-F449-E211-BB40-1040F3A7A3B1
[HttpPost]
public ActionResult Edit(Guid id, UserView user)
{
var response = Client.PutAsJsonAsync("users/" + id, user).Result;

if (response.StatusCode == HttpStatusCode.OK)
{
return RedirectToAction("Index");
}

AddModelErrors(response);

return View(user);
}

Similar methods are repeated for Delete i.e. Client.DeleteAsync("users/" + id).Result; and so on.

That is pretty much the building blocks for the entire UI application. Each controller is a very thin layer that co-ordinates model binding, calling the correct API resource and returning the views. It would be reasonable to question the need for any server side code within the UI for such an architecture as this is crying out to be either a single page app or a native app e.g. Windows 8 app, iOS, android etc.

Model Binding

A lot of the code in the MVC layer maps view model objects to API objects. For example, the following Get() method from the Users API controller returns a list of "GetUser" objects:


// GET users
public IEnumerable<GetUser> Get()
{
var users = _unitOfWork.UserRepository.GetAll();
return _mapper.Map<IEnumerable<User>, IEnumerable<GetUser>>(users);
}

To provide the option of statically typed goodness for the MVC project I pulled out the strongly typed request/response objects for the API layer into its own simple class library containing only the objects for data transfer.

For example, here is the GetUser class from the previous example.


[Serializable]
public class GetUser
{
public Guid ID { get; set; }

public string Username { get; set; }
}

I can simply add a reference to this project of simple objects, called .APIModel in this solution, to the MVC project and have the option of static typing. In the previous code examples I gave the types were being cast as dynamic when returned. I could keep things this way and design my views accordingly but without any static typing. This is down to personal preference and my preference is to create view model classes that can be used to scaffold out the views.

In order to use the statically typed approach some additional mapping code is added to replace the use of dynamic in the previous code snippets. This is a simple mapping from the returned object to the view model object:


var response = Client.PostAsJsonAsync("users", Mapper.Map<UserView, PostUser>(user)).Result;

OR


return View(Mapper.Map<GetUser, UserView>(response.Content.ReadAsAsync<GetUser>().Result));

NB the same approach to mapping with AutoMapper is used here. This was covered in this post under the heading "Mapping Entities".

For the full controller code see the UsersController.

The source code for this project can be found at: github.com/bbraithwaite/smsquiz.

The next, and final post of this series discusses Shipping Code.

SHARE
Don't miss out on the free technical content:

Subscribe to Updates

CONNECT WITH BRADLEY

Bradley Braithwaite Software Blog Bradley Braithwaite is a software engineer who works for the search engine duckduckgo.com. He is a published author at pluralsight.com. He writes about software development practices, JavaScript, AngularJS and Node.js via his website . Find out more about Brad. Find him via:
You might also like:
mean stack tutorial AngularJS Testing - Unit Testing Tutorials