When using WCF services sometimes there is a need to use REST architecture, especially for web applications. Using REST, brings a lot of benefits such as: broad caching possibilities, interoperability, simplicity etc. Also using REST with JSON instead of soap/xml based protocol decreases network bandwidth and makes it easier to utilize by JavaScript Ajax calls.
In this article I will show you how to retrieve and update data from WCF restful service using JSON protocol. In order to do that we will build request helper to wrap-up our requests sent from MVC application. We will also use JSONP data format to make cross-domain ajax calls to WCF service.
Let’s create MVC application and IIS hosted WCF service. In order to apply custom routing to our service, we will have to change following settings:
– set aspNetCompatibilityEnabled=”true” in WCF web.config file,
– set [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)] – to our service class,
We also need to set crossDomainScriptAccessEnabled=”true” in WCF web.config for our ajax calls from different domain (different localhost port).
Please see WCF web.config settings:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<authentication mode="None"/>
</system.web>
<system.serviceModel>
<bindings>
<webHttpBinding>
<binding name="webHttpBindingWithJsonP" crossDomainScriptAccessEnabled="true" />
</webHttpBinding>
</bindings>
<services>
<service name="WCFRestfulService.MyService" behaviorConfiguration="RESTBehavior">
<endpoint address="" binding="webHttpBinding" contract="WCFRestfulService.IRestService"
behaviorConfiguration="MyEndpointBehavior" bindingConfiguration="webHttpBindingWithJsonP">
</endpoint>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="RESTBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="MyEndpointBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true">
</serviceHostingEnvironment>
</system.serviceModel>
</configuration>
Next let’s create our ServiceContract and DataContract. Please note that we are defining UriTemplate to specified url contract routing, we will also use WebMessageFormat.Json attribute to declare the request format.
[ServiceContract(Namespace = "https://mysite.com/2013/06/08", Name = "WCFRestService")]
public interface IRestService
{
[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json,
UriTemplate = "Products?skip={skip}&top={top}")]
[Description("Returns a list of all products")]
List<Product> GetAllProducts(int skip, int top);
[OperationContract]
[WebInvoke(Method = "DELETE", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json,
UriTemplate = "DeleteProduct?productID={productID}")]
[Description("Deletes a product")]
string DeleteProduct(string productID);
[OperationContract]
[WebInvoke(Method = "POST", ResponseFormat = WebMessageFormat.Json, RequestFormat = WebMessageFormat.Json,
UriTemplate = "/UpdateProduct")]
[Description("Updates the product")]
string UpdateProduct(Product productDto);
}
[DataContract]
public class Product
{
[DataMember]
public string ProductID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Category { get; set; }
[DataMember]
public double Price { get; set; }
}
Also, in the service global file we need to define following routing rule.
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute("", new WebServiceHostFactory(), typeof(MyService)));
}
Our .svc file should contain following (Factory=”System.ServiceModel.Activation.WebScriptServiceHostFactory” added)
<%@ ServiceHost Language="C#" Debug="true" Service="WCFRestfulService.MyService" CodeBehind="MyService.svc.cs" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>
Inside our service class we will add following test methods:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class MyService : IRestService
{
List<Product> IRestService.GetAllProducts(int skip, int top)
{
var productList = new List<Product>();
var random = new Random();
#region simulate database
for (var i = 0; i < 100; i++)
{
productList.Add(new Product()
{
ProductID = i.ToString(),
Category = "Category " + i % 2,
Name = "Product " + i.ToString(),
Price = random.Next(10, 100)
});
}
#endregion
//////////////////////
var productSet = (from product in productList
orderby product.Name
select product).Skip(skip).Take(top).ToList();
return productSet;
}
string IRestService.DeleteProduct(string productID)
{
//delete from database here
return "OK";//return status or error
}
string IRestService.UpdateProduct(Product productDto)
{
//update the product here
return "OK";//return status or error
}
}
Next, in MVC application let’s create request helper that we will use to wrap-up our requests executed from C# controllers. Helper functions simply convert our data to JSON format when sending REST requests. We will also de-serialize objects from JSON to C# when retrieving the data.
public class RequestHelper
{
/// <summary>
/// Get object using GET
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="url"></param>
/// <returns></returns>
public static T GetObjectRest<T>(string url)
{
var request = HttpWebRequest.Create(url);
request.Method = "GET";
var response = request.GetResponse() as HttpWebResponse;
using (var resStream = response.GetResponseStream())
{
using (var reader = new StreamReader(resStream))
{
var jsonResponse = reader.ReadToEnd();
return FromJSON<T>(jsonResponse);
}
}
}
/// <summary>
/// Updates object
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static string UpdateObjectRest<T>(string url, T objectDto)
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/json";
//send the data.
using (var requestWriter = new StreamWriter(request.GetRequestStream()))
{
requestWriter.Write(ToJSON<T>(objectDto));
}
//read response
var response = (HttpWebResponse)request.GetResponse();
using (var resStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(resStream))
{
return reader.ReadToEnd();
}
}
}
/// <summary>
/// Send DELETE request
/// </summary>
/// <param name="url"></param>
/// <returns></returns>
public static string DeleteObjectRest(string url)
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "DELETE";
//read response
var response = (HttpWebResponse)request.GetResponse();
using (var resStream = response.GetResponseStream())
{
using (StreamReader reader = new StreamReader(resStream))
{
return reader.ReadToEnd();
}
}
}
/// <summary>
/// Deserialize json to custom object
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="json"></param>
/// <returns></returns>
public static T FromJSON<T>(string json)
{
var jss = new JavaScriptSerializer();
return jss.Deserialize<T>(json);
}
/// <summary>
/// Serializes object to json
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="objectDto"></param>
/// <returns></returns>
public static string ToJSON<T>(T objectDto)
{
var jss = new JavaScriptSerializer();
return jss.Serialize(objectDto);
}
}
Calling Rest service will look as follows:
//get list of products
var productList = RequestHelper.GetObjectRest<List<Product>>("https://localhost:9582/Products?skip=0&top=10");
Retrieving data using client side Ajax request is quite difficult because of the cross-domain policy (CORS) disabling web script access by default. The way around that is to use JSONP data format so the server is treating our request as a script call allowing us to return data back inside the call-back function. The limitation of that is that we can only use GET method to retrieve the data. If you need full access to the WCF restful service, it is recommended to make your Ajax requests from the same domain.
<script type="text/javascript">
function GetProducts() {
$.ajax({
type: "GET", //only 'get' is supported with 'jasonp' datatype
url: "https://localhost:9582/Products?skip=0&top=10",
dataType: "jsonp",
data: '',
success: function (result) {
alert('Received: ' + result.length + ' products');
},
error: function (xhr, ajaxOptions, thrownError) {
// alert(xhr.status);
// alert(thrownError);
}
});
}
</script>
Please find the project files included below.
WCFRestfulService