Creating an async block in EPiServer

Blocks are reusable components within a webpage that were introduced with the release of EPiServer 7. It was a major step forward for EPiServer as it shifted from page based content model to actual usage of content based model. The purpose of this blog is to demonstrate the implementation of an asynchronous block in an EPiServer webpage. 

The block will get the data from a controller using the $http service of AngularJS and the controller itself will do async calls to an external web api.

Let’s assume that you have installed already Alloy Mvc Templates. First go to Global.asax.cs and add the following code to handle the routing for blocks.

RouteTable.Routes.MapRoute("DefaultControllerAction", "{controller}/{action}");
            RouteTable.Routes.MapRoute("Car", "car/{action}/{id}",
                new { controller = "Car", action = "GetCars", id = "" });

Then create a new block that inherits SiteBlockData. No properties are required. Create also the model (which will be empty) and the view for the block. The view should be something like the folllowing.

The “app” tag is missing so I have to add that too. On the _Root view on the html tag add ng-app=”AsyncPoc”. We are missing the angular script and the controller. On the controller we first need to inherit from AsynController. For the method to be async both the “async” word in the beginning and the “await” are needed for it to work asynchronously.

A requirement for our example is the connection is to be achieved through https. To achieve this I have setup a simple web api using the example project in visual studio. I have added the project in my local IIS and added the https binding using the IIS Express Development Certificate. Make sure that you have the certificate added in the root of the trusted certificates in your machine. Notice the comment in the code that is used to bypass security issues with SSL. It is only used for demonstrations purposes. Do not use it when you have your own certificate for your site but instead make sure that there are no errors in the connection. Also in the client initialization I have added a key, value pair in the web.config from which I get the path i.e. https://asynpocapi.myshosting.com

public class CarController : AsyncController
{
	[HttpGet]
	public async Task<ActionResult> GetCars(string id)
	{
		// Initialize Client
		var client = new HttpClient {BaseAddress = new Uri(ConfigurationManager.AppSettings["AsyncPocApi"])};
		client.DefaultRequestHeaders.Accept.Clear();
		client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

		var cars = new List<Car>();

		// Bypass certificate issues with iis express dev certificate
		System.Net.ServicePointManager.ServerCertificateValidationCallback =
			((sender, certificate, chain, sslPolicyErrors) => true);

		try
		{
			var response = await client.GetAsync("api/cars");
			if (response.IsSuccessStatusCode)
			{
				var carsString = await response.Content.ReadAsStringAsync();
				//Covert string obj to list of cars and return as succesfull
				cars = JsonConvert.DeserializeObject<List<Car>>(carsString);
				return Json(new {Success = true, Cars = cars}, JsonRequestBehavior.AllowGet);
			}
		}
		catch (Exception ex)
		{
		}

		return Json(new {Success = false, Cars = cars}, JsonRequestBehavior.AllowGet);
	}
}

Let’s also add a car class.

public class Car
{
	public string Brand { get; set; }
	public string Model { get; set; }
	public string Price { get; set; }
}

Finally let’s create a new javascript that will handle the angular code. The implementation of service is needed to retrieve the data from the controller (in similar way as ajax in jquery). Keep in mind that the way the scope works in this script there is no problem to have more than one controllers in the same page.

(function() {
    var app = angular.module("AsyncPoc", []);
    app.controller("AsyncPocController", function($scope, asyncPocService) {
        $scope.cars = [];
        
        asyncPocService.getCars().then(function(newCars) {
            $scope.cars = newCars;
        });
    });

    app.service("asyncPocService", function($http, $q) {
        return ({
            getCars: getCars
        });

        function getCars() {
            var request = $http({
                method: "get",
                url: "car/getcars",
                params: {
                    action: "get"
                }
            });
            

            return (request.then(handleSuccess, handleError));

            function handleError(response) {
                if (!angular.isObject(response.data) || !response.data.message) {
                    return ($q.reject("An unknown error occurred."));
                }

                return ($q.reject(response.data.message));
            }

            function handleSuccess(response) {
                return (response.data.Cars);
            }
        }
    });
}())

It is outside the scope of this blog to show the code for the Web Api but it is pretty simple to create one based on the example of the Visual Studio and with the purposes of this post. 

When all is done you can be able to add one or more blocks of this type in the same page to perform async calls to an external web api.