Batching in an ASP.NET Core 1.0 AngularJS application using angular-http-batcher

This article shows how to use angular-http-batcher in an ASP.NET 5 application. The back end is implemented using the existing Web API ASP.NET framework. At present, batching is not supported in the new ASP.NET Core 1.0 framework. It looks like batching will not be supported in the new MVC framework. A lot of the existing Web API features are missing in the new MVC framework.

Code: https://github.com/damienbod/AspNet5BatchingMiddleware

2015.09.20: Updated to ASP.NET Core 1.0 beta 7

Server side batching

Batching on the server side is implemented using the default batching in a Visual Studio 2013 application. The code can be found here. This server has two simple controllers and implements the batch handler as follows:

 config.Routes.MapHttpBatchRoute(
  routeName: "batch",
  routeTemplate: "api/batch",
  batchHandler: new DefaultHttpBatchHandler(GlobalConfiguration.DefaultServer)
);

Now the server is ready to process batch requests.

Batching client

The batching client is implemented using angular-http-batcher in an ASP.NET 5 application. The ui-router resolve methods are sent to the server using the batch service. The dependencies are added using grunt and bower. The bower file is configured as follows:

{
	"name": "WebApplication",
	"private": true,
	"dependencies": {
		"angular": "1.3.15",
		"angular-ui-router": "0.2.14",
		"angular-http-batcher": "1.7.0"
	},
	"exportsOverride": {
	}
}

Once the dependencies have been downloaded and added using grunt, the files need to be added in the root or index html file inside the wwwroot folder:

<!DOCTYPE html>

<html>
<head>
    <meta charset="utf-8" />
    <title>AngularJS ASP.NET5 batching</title>
</head>
<body>
	<div ng-app="myapp">
		<h4 class="panel-heading">Index Layout page</h4>
		<hr />
		<div ui-view></div>
	</div>

	<script src="lib/angular/angular.js"></script>
	<script src="lib/angular-ui-router/angular-ui-router.js"></script>
	<script src="lib/angular-http-batcher/angular-http-batch.min.js"></script>
	<script src="app.js"></script>
</body>
</html>

The app.js is the file which contains all of the angularJs javascript which is created using the grunt uglify method. Grunt is configured as follows:

module.exports = function (grunt) {

	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-watch');

	grunt.initConfig({
		bower: {
			install: {
				options: {
					targetDir: "wwwroot/lib",
					layout: "byComponent",
					cleanTargetDir: false
				}
			}
		},
		uglify: {
			my_target: {
				files: { 'wwwroot/app.js': ['app/app.js', 'app/**/*.js'] }
			}
		},
		watch: {
			scripts: {
				files: ['app/**/*.js'],
				tasks: ['uglify']
			}
		}
	});

	grunt.registerTask("default", ["bower:install"]);
	grunt.registerTask('default', ['uglify', 'watch']);

	grunt.loadNpmTasks("grunt-bower-task");
};

Note: before you use uglify and watch, you need to download these using npm. This is configured in the package.json file:

{
    "version": "0.0.0",
    "name": "",
    "devDependencies": {
        "grunt": "0.4.5",
		"grunt-bower-task": "0.4.0",
		"grunt-contrib-uglify": "0.8.0",
        "grunt-contrib-watch": "0.6.1"
    }
}

Now that all the required libraries are added, the client code can be implemented. First a service is implemented to access the server service TestClassOneController. This service just wraps the HTTP GET request. The same is implemented for the TestClassTwoController service of the server application.

(function () {
	'use strict';

	function TestClassOneService($http, $log) {

		$log.info("TestClassOneService called");

		var getAll = function () {
			$log.info("TestClassOneController getAll called");
			return $http.get("http://localhost:13605/api/TestClassOne")
			.then(function (response) {
				return response.data;
			});
		}

		return {
			getAll: getAll
		}
	}

	var module = angular.module('myapp');

    module.factory("TestClassOneService",
		[
			"$http",
			"$log",
			TestClassOneService
		]
	);
})();

Then the routing is defined using ui-router. This demo application has only one state which resolves both the required resources from the server before loading the template.

app.config(["$stateProvider", "$urlRouterProvider",
	function ($stateProvider, $urlRouterProvider) {
		$urlRouterProvider.otherwise("/home");
		$stateProvider.state("home", {
			url: "/home", templateUrl: "/templates/home.html", controller: "homeController",
			resolve: {
				TestClassOneService: "TestClassOneService",

				testClassOne: ["TestClassOneService", function (TestClassOneService) {
					return TestClassOneService.getAll();
				}],
				TestClassTwoService: "TestClassTwoService",

				testClassTwo: ["TestClassTwoService", function (TestClassTwoService) {
					return TestClassTwoService.getAll();
				}]
			}
		});
	}
	]);

Now the batching can be added. The httpBatchConfigProvider defines the root URL for batching and also the batching URL. These are the URLs of the server application. The URL path matches the root URL in the getAll methods.

app.config(['httpBatchConfigProvider', function (httpBatchConfigProvider) {
		httpBatchConfigProvider.setAllowedBatchEndpoint(
			// root endpoint url
			'http://localhost:13605/api',

			// endpoint batch address
			'http://localhost:13605/api/batch',

			// optional configuration parameters
			{
				maxBatchedRequestPerCall: 20
			});
		}
	]);

The batching module is added as follows inside an array so that the uglify works.

var app = angular.module('myapp', [
		'ui.router',
		'jcs.angular-http-batch'
	]);

When the application is run, the resolve methods are executed inside a single batch request.
angularjsBatching_01

The result of the resolve are added to the controller of the home state in the constructor and added to the scope.

(function () {
	'use strict';

	var module = angular.module('myapp');

	module.controller('homeController',
		[
			'$scope',
			'$http',
			'$log',
			'testClassOne',
			'testClassTwo',
			homeController
		]
	);

	function homeController($scope, $http, $log, testClassOne, testClassTwo) {
		$scope.testClassOne = testClassOne;
		$scope.testClassTwo = testClassTwo;
		console.log('home controller called');
	}
})();

The model is then used in the home template to display the results:

<div>testClassOne</div>
<div>
	<ul>
		<li ng-repeat="(Id,Description) in testClassOne">{{Description}}</li>
	</ul>
</div>

<div>testClassTwo</div>

<div>
	<ul>
		<li ng-repeat="(Id,News) in testClassTwo">{{News}}</li>
	</ul>
</div>

The result can then be viewed in the browser
angularjsBatching_02

Conclusion

Batching is really important when using RESTful services. No one likes a slow UI, so sequential loading of resources is a major no go. I hope that ASP.NET 5 will implement batching as this is a major requirement for me when implementing user friendly UIs so that the backend requests can be optimized.

Links:

https://github.com/jonsamwell/angular-http-batcher

http://jonsamwell.com/batching-http-requests-in-angular/

https://github.com/aspnet/Mvc/issues/508

https://github.com/aspnet/Home/issues/262

https://github.com/aspnet/HttpAbstractions/issues/139

3 comments

  1. […] Batching in an ASP.NET 5 AngularJS application using angular-http-batcher – damienbod takes a look at implementing batched requests from AngularJS application, and how they are supported on the current ASP.NET framework, but not currently on the new ASP.NET MVC. […]

  2. […] This article shows how to use angular-http-batcher in an ASP.NET 5 application. The back end is implemented using the existing Web API ASP.NET framework. At present, batching is not supported in th…  […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: