Introduction
This is an end-to-end description of how in Visual Studio 2015 to create and stand-up a WCF Restful service that can be consumed by AngularJS
Background
I am writing this article because I could not find a single stand alone example to create and run a simple but complete WCF/JSON/AngularJS app in Visual Studio 2015.
Instead I found several good articles that addressed various parts of writing and consuming JSON, or Rest in WCF, or AngularJS.
Consequently, I created this article from several others, as well as adding my own experience in making applications work. Wherever I have lifted code, I tried to site the appropriate source with the author's URL.
Using the code
This is a cook book. Like all recipes, I am only laying a working foundation so that you can add your own flavoring to this model to meet your immediate needs.
Creating a Restful Service with VS2015
Create the app
Open VS2015, open a new project and select WCF Service Application.
I named my project RestSample and I named the project WcfRestfulService.
Add a new WCF service to the project
Add a new service and interface, by right clicking on the project WcfRestfulService, and select WCF Service
I named the service ProductService.svc. Also you can delete the existing IService and Service.svc files since we are not using them.
Create a data model
This step is optional since you can simply pass a JSON string latter on. Still this shows how to create a JSON formatted string from a net class using serialization, which is very common.
To create the data model, first create a folder (I called mine Domain) that will host our data service. You can do this by right-clicking the project file and selecting Add New Folder.
In the folder add a class, by right-clicking the folder, click Add, select Add class
I named my file and class Product
. Following is the content of the class, prepared for serialization in WCF.
[DataContract]
public class Product
{
[DataMember]
public int ProductId { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string CategoryName { get; set; }
[DataMember]
public int Price { get; set; }
}
Next I created a static class called ProductServer.cs that will be used to create and provide the list of products.
public sealed class ProductsServer
{
private static List<Product> _products;
private static ProductsServer _instance;
private static readonly object LockMechanism = new object();
public static ProductsServer Instance
{
get
{
if (_instance == null)
{
lock (LockMechanism)
{
_instance = new ProductsServer();
}
}
return _instance;
}
}
private ProductsServer()
{
Intialize();
}
private static void Intialize()
{
_products = new List<Product>
{
new Product() {ProductId = 1, Name = "Product 1", CategoryName = "Category 1", Price = 10},
new Product() {ProductId = 2, Name = "Product 2", CategoryName = "Category 1", Price = 5},
new Product() {ProductId = 3, Name = "Product 3", CategoryName = "Category 2", Price = 15},
new Product() {ProductId = 4, Name = "Product 4", CategoryName = "Category 3", Price = 9}
};
}
public List<Product> Products
{
get { return _products; }
}
}
Modify the IProductService with Web Protocols
In IProductService
, delete the DoWork
function and replace it with GetProductList
.
Next add the WebInvoke
decoration, so that java based callers (like AngularJS and AJAX) can read our JSON formatted product list
[ServiceContract]
public interface IProductService
{
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json,
RequestFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = "GetProductList/")]
List<Product> GetProductList();
}
In the ProductService
class add a reference to the ProductServer
we created earlier to fetch the list.
public class ProductService : IProductService
{
List<Product> IProductService.GetProductList()
{
return ProductsServer.Instance.Products;
}
}
As Mr. Ghani may have noticed this is very similar to his page, which in fact I am using as a guide. His excellent article can be found at TopWcfTutorials.
Code Clean-up
At this point, the product should be a complete WCF service; on the other hand, I always seem to have issues that need to be resolved.
To test our work, right click on the svc and select view in browser.
When I tried this I got the following error:
The type 'WcfService1.ProjectService', provided as the Service attribute value in the ServiceHost directive, or provided in the configuration element system.serviceModel/serviceHostingEnvironment/serviceActivations could not be found.
As the error says, I forget to change the namespace, an easy error to make, which is why I am leaving it in.
To resolve the issue, right click on the ProductService.svc, select View Markup, and open with the XML (text) Editor. You will see that the Service is still labeled as WcfService1. Change that to WcfRestulService.
While I was doing that I went back into my project file and changed the default Assembly name and Namespace (right-click project file. Select properties)
Now I have a new error.
OperationContractAttributes are only valid on methods that are declared in a type that has ServiceContractAttribute.
Looking in Web.config, I find there are no end points or behaviours, so we need to build-out the configuration.
Refactor the Web.config with endpoints, services and behaviors
Add the binding type in the protocol mapping to webHttpBinding
Add the behaviors
Add the service end points
We now have a working service. Unfortunately, it is SOAP and we want Rest, so we add the following behavior extension.
Next we need to reference the extension in the endpoint behaviors.
Finally we need to add a class to handle cross platform service calls. This class is taken from CORS plus JSON Rest
Adding a CORS extension
Right-click on the project and add a new file to enable CORS - Cross Origin Resource Sharing. Enabling CORS will allow us to see the JSON string directly in the browser, and it will give AngularJS the permission neccessary to read the restful service.
public class CORSEnablingBehavior : BehaviorExtensionElement, IEndpointBehavior
{
public void AddBindingParameters(
ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters)
{ }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(
new CORSHeaderInjectingMessageInspector()
);
}
public void Validate(ServiceEndpoint endpoint) { }
public override Type BehaviorType { get { return typeof(CORSEnablingBehavior); } }
protected override object CreateBehavior() { return new CORSEnablingBehavior(); }
private class CORSHeaderInjectingMessageInspector : IDispatchMessageInspector
{
public object AfterReceiveRequest(
ref Message request,
IClientChannel channel,
InstanceContext instanceContext)
{
return null;
}
private static IDictionary<string, string> _headersToInject = new Dictionary<string, string>
{
{ "Access-Control-Allow-Origin", "*" },
{ "Access-Control-Request-Method", "POST,GET,PUT,DELETE,OPTIONS" },
{ "Access-Control-Allow-Headers", "X-Requested-With,Content-Type" }
};
public void BeforeSendReply(ref Message reply, object correlationState)
{
var httpHeader = reply.Properties["httpResponse"] as HttpResponseMessageProperty;
foreach (var item in _headersToInject)
httpHeader.Headers.Add(item.Key, item.Value);
}
}
}
Testing the service
At this point we should be able to see our product list serialized as JSON in our browser. You can do this by right-clicking ProductService.svc as we have been doing, and by adding making the call to the service /GetProductList
In my browser it looks like this:
Create an AngularJS App to Consume the Service
Add an empty ASP.Net Web Application
On the solution file, right-click and select Web, ASP.NET Web Application. I called mine WebApp.
Next select the empty template, and click OK.
Add an Index.html by right clicking the project file
Load the AngularJS Libraries
If you do not already have the AngularLS library files, open the Package Manager Console. You will find it under View->Other Windows->Package Manager Console.
Load the AngularJS files directly from NuGet by typing in the following command at the promt PM>
Install-Package AngularJS.Core
This adds the AngularJS core library into the scripts folder. I had to open and close the folder a couple of times to see the change, but following is an image of scripts after loading the library.
Create the AngularJS app and module files
Start by creating a folder in WebApp named app.
I am going to separate the app and the module files as is the generally recommended practice.
Create two java script file named app.module.js and main.js, by right-clicking on the app folder and select Add, then JavaScript File.
Add the following code to app.module.
(function () {
'use strict';
angular.module('app', []);
})();
Add the following in the main. You will need to modify the URL to use the same port as your service WcfRestfulService
(function () {
'use strict';
angular
.module('app')
.controller('Main', main);
function main($scope, $http) {
$http.get('http://localhost:62243/ProductService.svc/GetProductList').then(function (response) {
$scope.products = response.data;
});
}
})();
You will find the port number by clicking on the service and looking at the URL in the Development Server section. In my case it is 62245. It will be different on each machine.
To test the service you should be able to copy or click on the url and see the service as we did earlier.
Reference your AngularJS files
We are finally ready to connect the WebApp to the service. To do this we need to reference the js classes in the Index.html, so that it can find the AngaularJS libraries and modules.
The page we are going to build is very simple. At the same time it completes the project from start to finish.
To bind to the app and controll we need to add the application directive and the controller directive in the body
Finally we need to create a table and bind it to the JSON data provided by our service using the repeat directive.
Don't forget, as I did, to set the WebApp as the startup project, by right-clicking the project file and selecting Set as StartUp Project.
The simple results are show below.
History
22 Feb 2016 - Submitted