Let’s continue the implementation of our cloud based viewer. Today we are going to add RESTful support to our service. Thanks to WCF, this is going to be very straightforward!
There are number of reasons why you would like to provide a REST API from your web services. Nowadays most of the public web services will provide REST access to their clients, so it is very popular, if not the dominant web service design model. Main advantages of REST are the following:
- Lightweight – heavy xml formatting of SOAP removed
- Human Readable Results
- Easy to build - no toolkits required
- Supported by a broad range of clients – mobile platforms, web browsers, …
The data format we will use for our REST API is JSON, a lightweight data-interchange format based on a subset of the JavaScript Programming Language.
I – Adding REST support to a WCF Service
The WCF Framework makes it very easy to support REST, all we have to do is prefixing the declaration of our OperationContract methods with the WebInvoke attribute and set the appropriate fields.
I created a new Interface that exposes our two methods in order to access available cloud models and retrieve data for a given modelId. Here is how they look like:
[ServiceContract]
interface IAdnCloudViewerSrvRest
{
// Returns list of all cloud hosted models
[OperationContract]
[WebInvoke(
Method = "GET",
UriTemplate = "/GetDbModelInfoRest",
ResponseFormat = WebMessageFormat.Json)]
ModelInfo[] GetDbModelInfoRest();
// Returns model data for a specific model
[OperationContract]
[WebInvoke(
Method = "GET",
UriTemplate = "/GetDbModelRest/{modelId}",
ResponseFormat = WebMessageFormat.Json)]
byte[] GetDbModelRest(string modelId);
}
The actual implementation of this interface is easy, as we can simply reuse the existing SOAP methods:
public class AdnCloudViewerSrv :
IAdnCloudViewerSrv,
IAdnCloudViewerSrvRest,
IAdnCloudViewerNotification
{
// ... Previous implementation ...
public ModelInfo[] GetDbModelInfoRest()
{
return GetDbModelInfo();
}
public byte[] GetDbModelRest(string modelId)
{
return GetDbModel(modelId);
}
}
-
Done with the code, we now have to set up our config file so our service supports REST/Json. We can of course conserve the existing Net.Tcp endpoints we created in the previous post, so none of our existing clients are broken and add a new endpoint using the REST interface.
I highlighted the changes I’ve made to the config file below:
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding
name="AdnCloudServicesBinding"
transferMode="Streamed"
messageEncoding="Mtom"
maxBufferSize="2147483647"
maxBufferPoolSize="2147483647"
maxReceivedMessageSize="2147483647">
<readerQuotas
maxDepth="200"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxNameTableCharCount="2147483647"/>
</binding>
</basicHttpBinding>
<netTcpBinding>
<binding name="TcpBinding"
hostNameComparisonMode="StrongWildcard"
sendTimeout="00:10:00"
maxReceivedMessageSize="65536"
transferMode="Buffered"
portSharingEnabled="false">
<readerQuotas
maxDepth="200"
maxStringContentLength="2147483647"
maxArrayLength="2147483647"
maxBytesPerRead="2147483647"
maxNameTableCharCount="2147483647"/>
<security mode="None">
<transport clientCredentialType="None"/>
<message clientCredentialType="None"/>
</security>
</binding>
</netTcpBinding>
<webHttpBinding>
<binding
name="webHttpBindingWithJson"/>
</webHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="AdnCloudServicesBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="JsonBehavior">
<webHttp/>
</behavior>
</endpointBehaviors>
</behaviors>
<services>
<service
behaviorConfiguration="AdnCloudServicesBehavior"
name="AdnCloudViewerService.AdnCloudViewerSrv">
<endpoint
address="mex"
binding="mexTcpBinding"
contract="IMetadataExchange"/>
<endpoint
name="WCFCHttpEndpoint"
address=""
binding="basicHttpBinding"
bindingConfiguration="AdnCloudServicesBinding"
contract="AdnCloudViewerService.IAdnCloudViewerSrv"/>
<endpoint
name="WCFNetTcpEndpoint"
bindingConfiguration="TcpBinding"
binding="netTcpBinding"
contract="AdnCloudViewerService.IAdnCloudViewerSrv"
address="Viewer"/>
<endpoint
name="WCFCallbackEndpoint"
bindingConfiguration="TcpBinding"
binding="netTcpBinding"
contract="AdnCloudViewerService.IAdnCloudViewerNotification"
address="Callback"/>
<endpoint
name="WCFRestClientEndpoint"
address="rest"
binding="webHttpBinding"
behaviorConfiguration="JsonBehavior"
bindingConfiguration="webHttpBindingWithJson"
contract="AdnCloudViewerService.IAdnCloudViewerSrvRest"/>
</service>
</services>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true "/>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
</configuration>
That should be it on the service side. We can now redeploy our service package in IIS on our cloud hosted machine as we did in the previous posts.
-
II – Implementing a REST desktop client
-
Let’s now implement the client side: no specific library is needed in order to perform REST web requests, it’s all built-in in WCF, however formatting the Json messages can be leveraged by the use of one of the numerous third party API. You can an idea of how many toolkits exist to work with Json by having a look at this page: http://www.json.org/. It lists the various libraries available classified by programming language.
-
As I’m working with .Net for the moment, I picked up this one, which seemed to have efficient processing time, in addition to be very user friendly: http://james.newtonking.com/projects/json-net.aspx
-
We of course want to keep our webservice calls asynchronous, so the exact same architecture can be used on the client side than what we had previously:
-
private void BeginRefreshCloudModels()
{
_tvModels.Nodes.Clear();
_modelIdNodeMap.Clear();
TreeNode root = _tvModels.Nodes.Add(
"_cloudModelsKey", "Cloud Models", 0, 0);
HttpWebRequest request = WebRequest.Create(
"http://" + _hostAddress +
"/AdnCloudViewer/AdnCloudViewerSrv.svc/rest/GetDbModelInfoRest")
as HttpWebRequest;
request.BeginGetResponse(
new AsyncCallback(GetDbModelInfoAsyncResponder),
request);
}
private void EndRefreshCloudModels(ModelInfo[] infos)
{
TreeNode root = _tvModels.Nodes[0];
foreach (ModelInfo info in infos)
{
if (!_modelIdNodeMap.ContainsKey(info.ModelId))
{
TreeNode node = root.Nodes.Add("", info.ModelId, 1, 1);
node.Tag = info;
_modelIdNodeMap[info.ModelId] = node;
}
}
root.Expand();
}
private delegate void RefreshCloudModelsDelegate(ModelInfo[] infos);
Here is how to handle the web response. You can see how easy it is to deserialize the Json message using JsonConvert static method from the Json.Net toolkit:
-
private void GetDbModelInfoAsyncResponder(IAsyncResult result)
{
try
{
using (HttpWebResponse response =
(result.AsyncState as HttpWebRequest).EndGetResponse(result)
as HttpWebResponse)
{
if (result.IsCompleted)
{
StreamReader reader = new StreamReader(
response.GetResponseStream());
string jsonMsg = reader.ReadToEnd();
var infos =
JsonConvert.DeserializeObject<List<ModelInfo>>(jsonMsg);
this.Invoke(
new RefreshCloudModelsDelegate(EndRefreshCloudModels),
new object[] { infos.ToArray() });
}
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
}
}
We do pretty much the same for the GetDbModelRest call and we end up with a complete REST client for our service, so I let you refer to the attached project for a deeper look into the details.
-
-
That’s it for now. In the next post I will show you how to create an Android client able to consume that REST service and display our cloud models!
Comments
You can follow this conversation by subscribing to the comment feed for this post.