One of the growing needs of these days, is the requirements of exposing services that are available to multiple platforms. This request come obviously from the increasing availability of a number of mobile platforms that is forcing companies to adopt strategies to decrease the complexity and use technologies that are able to respect the verb "write once and use everywhere".
Javascript object notation - or as everyone know for sure, JSON - is something that has emerged as the common denominator, when the need is to expose services and APIs that may be easily consumed by multiple platforms. JSON is a simple notation - born long before the birth of the today's mobile world - that is able to describe object instances with a javascript syntax. The primary feature of JSON is the compactness, that is better than any other text based serialization strategy and many times better than XML. JSON is made of a combination of curly and square braces, of comma, colon and quotes. Explaining the syntax of JSON is out of the scope of this article but here is a basic example of the notation:
1: { "FirstName" : "Andrea", "LastName" : "Boschin", "Age" : 43 }
This notation, as you can easily understand, describes a Person instance that exposes FirstName, LastName and Age properties. JSON let you describe objects and arrays and it always remain compact in size. The reason of its popularity is not only about compactness, but comes from the fact that every platform, mobile or not, has a way to serialize and deserialize this smart notation.
Windows Phone is not different in this. Including the System.ServiceModel.Web.dll you have a DataContractJsonSerializer class that is able to perform the required conversion between JSON to plain objects and viceversa. But what about if you have to create a json service and consume it with Windows Phone? For us, developers that usually take advantage of great Visual Studio IDE, able to generate code on behalf of us, using a json service may be matter of give up of helpers and write all by hand.
Architect your service
So let say you have the common need of expose a set of APIs from your website and obviously you want to create a native app for Windows Phone that have to consume these APIs. The requirement is to prepare the connection points to be ready to be consumed by other devices so you want to use JSON as output.
The first thing to do is configure your service. WCF is a good starting point because, thanks to the WebHttpBinding, you can write services, with the common constraints you already know and then configure an endpoint to accept calls made with json. The starting point, as usual is to create the contract:
1: [ServiceContract]
2: [ServiceKnownType("GetKnownTypes", typeof(KnownTypesProvider))]
3: public interface ITestService
4: {
5: [OperationContract]
6: [WebInvoke(
7: Method = "POST",
8: BodyStyle = WebMessageBodyStyle.Wrapped,
9: RequestFormat = WebMessageFormat.Json,
10: ResponseFormat = WebMessageFormat.Json)]
11: Article GetArticleById(int id);
12:
13: // add here other methods...
14: }
In this snippet there are two important details. The service contract is decorated with the ServiceKnownType attribute that is useful to instruct your service about the types you need to serialize and deserialize. This attribute indicate a type to be the source of the types WCF has to know. This class has to expose the "GetKnownTypes" method:
1: public static class KnownTypesProvider
2: {
3: public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
4: {
5: yield return typeof(Article);
6:
7: // yield here other types...
8: }
9: }
The code returns the Article type, but you have to add all other types that have to cross the service boundary, particularly if this type is not directly the return type of a method.
The other important thing to note is the WebInvoke attribute. This attribute, applied to the operations, determine the way the input and output will be serialized. In this sample I use WebMessageFormat.Json for both RequestFormat and ResponseFormat, meaning it will return and accept data formatted with json. Another important attribute is BodyStyle. Setting this argument as "Wrapped" means that we want WCF to encapsulate arguments in a envelope that describe the signature of the method. This wrapper contains the name of the method, and the name of the parameters. Here is an example ot the input and output of the above method:
1: // Signature
2:
3: Article GetArticleById(int id);
4:
5: // Input Message
6:
7: {"id":27}
8:
9: // Output Message
10:
11: {"GetArticleByIdResult":{"Id":27,"MediumVote":0,"Title":"test","VoteCount":0}}
Once the contract has been prepared now you can implement all the methods you need. In the example attached to the article you will find all the needed code. The most important thing is the configuration of the service, in the Web.Config. This chunk of code specified the connection endpoint and some important details about the nature of the binding and transport:
1: <system.serviceModel>
2:
3: <services>
4: <service name="XPG.WindowsPhoneJson.Services.TestService">
5: <endpoint address=""
6: binding="webHttpBinding"
7: contract="XPG.WindowsPhoneJson.Services.ITestService"
8: behaviorConfiguration="ITestService_WebHttp" />
9: </service>
10: </services>
11:
12: <behaviors>
13: <endpointBehaviors>
14: <behavior name="ITestService_WebHttp">
15: <webHttp />
16: </behavior>
17: </endpointBehaviors>
18: </behaviors>
19:
20: </system.serviceModel>
The keys of this configuration are the webHttpBinding assigned to the binding attribute and the <webHttp /> element. These two configurations enable our service to accept calls with simple POST and make the service completely interoperable with a number of platforms.
Architect your App
Moving to the Windows Phone application, when all the methods of the services are implemented, you cannot simply generate a proxy as you do usually with services. To simplest way to consume the json service is to make POST calls using the WebClient class. This means write the json message, prepare the string to send to the service and then place the call. Then, when the service returns a value you have to make the deserialization. I really love to write code, but I do not like to write the same code lot of times to do always the same thing. So finally I put together a bunch of classes that simplify my work.
My solution is made of two classes: Channel is responsible of manage the communication between the client and server. It initializes the WebClient, sets the required headers and then send the message along the wire. Then, when the answer returns, it check for errors and cleanup the resources. On the other side the JsonSerializer use a DataContractJsonSerializer to prepare the message to send and read the returning answer:
1: public void Call<K>(string method, object args, Action<K> success, Action<Exception> fail)
2: {
3: string message = this.Parser.Serialize(args);
4: Uri uriToCall = new Uri(this.Uri.AbsoluteUri + "/" + method);
5:
6: this.Client.Headers[HttpRequestHeader.ContentType] = "application/json";
7:
8: UploadStringCompletedEventHandler handler = null;
9:
10: handler = (s, e) =>
11: {
12: this.Client.UploadStringCompleted -= handler;
13:
14: if (e.Error != null)
15: fail(e.Error);
16: else
17: {
18: K result = this.Parser.Deserialize<K>(method, e.Result);
19: success(result);
20: }
21: };
22:
23: this.Client.UploadStringCompleted += handler;
24: this.Client.UploadStringAsync(uriToCall, message);
25: }
The above method, is from the Channel class. as you can see it accept a method name (the name of the service operation to call) and an args parameter. This argument, of type object, is used to easily pass the arguments using anonymous types. Then the method prepares the uri, attaching the method name to the base uri of the service. Finally it sets the application/json content-type and place the call. The JsonSerializer class collaborate with the Call method:
1: public T Deserialize<T>(string method, string message)
2: {
3: Match match = Regex.Match(message, @"{""" + method + @"Result"":(?<inner>.*)}");
4:
5: if (match.Success)
6: return FromJson<T>(match.Groups["inner"].Value);
7:
8: throw new FormatException("Message format is not valid");
9: }
10:
11: public string Serialize(object args)
12: {
13: Type type = args.GetType();
14: StringBuilder builder = new StringBuilder();
15: bool isFirst = true;
16:
17: builder.Append("{");
18:
19: foreach (PropertyInfo property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
20: {
21: object value = property.GetValue(args, null);
22: if (!isFirst) builder.Append(","); else isFirst = false;
23: builder.AppendFormat("\"{0}\":{1}", property.Name, this.ToJson(value));
24: }
25:
26: builder.Append("}");
27:
28: return builder.ToString();
29: }
The trick is done creating the right wrapping code. The DataContractJsonSerializer simple convert the object to json but it does not directly handles the wrapped format of the message for the json service. So, if in the Deserialize method I strip away the wrapper just before of passing the message to the DataContractJsonSerializer , in the Serialize method I read the properties of the args value and I add the wrap calling the DataContractJsonSerializer of each parameter.
Unfortunately, the DataContractJsonSerializer needs to know the types that it has to serialize or deserialize. In a normal web service this problem is handled generating a copy ot the DataContracts on the client side, but her we have not a copy of these classes. So we have to manage add a copy of the data contracts to the client solution. This is done creating a link between the Windows Phone application project and the Service application project. In a real world example you shoud generate data contract in a specific assembly and then link these classes in another assembly, compiled for Windows Phone.
In the figure on the left side you see the solution where the Article and KnowTypesProvider class are defined in the server assembly and linked in the WP7 class library. The important thing to note is that the base namespace for both the classes is the same "XPG.WindowsPhoneJson.Entities" because I've removed the "WP7" part in the settings. After you linked the assemblies on the respective part (service and client) you can instruct the DataContractJsonSerializer to use them:
1: private static T FromJson<T>(string json)
2: {
3: DataContractJsonSerializer dcjs =
4: new DataContractJsonSerializer(typeof(T), KnownTypesProvider.GetKnownTypes(null));
5:
6: using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
7: return (T)dcjs.ReadObject(stream);
8: }
9:
10: private string ToJson(object value)
11: {
12: DataContractJsonSerializer dcjs =
13: new DataContractJsonSerializer(value.GetType(), KnownTypesProvider.GetKnownTypes(null));
14:
15: using (MemoryStream stream = new MemoryStream())
16: {
17: dcjs.WriteObject(stream, value);
18: return Encoding.UTF8.GetString(stream.ToArray(), 0, (int)stream.Length);
19: }
20: }
Write your own proxy
When you generate code from a service endpoint, side by side with your datacontracts, you get the main wrapper for the service under the form of a class that is usually called proxy. It main scope is to create an abstraction of the service to provide a way to consume its methods seamless. Once the Channel class has been build, is become very easy to write a proxy for a json service. Thanks to the structure I've illustrated it is a matter of write a single line of code, in a method that serves as a way to make parameters more user friendly. Here is a simple service wrapper based on the Channel class:
1: public class TestService : Channel<JsonSerializer>
2: {
3: public TestService(Uri uri)
4: : base(uri)
5: { }
6:
7: public void GetArticleById(int id, Action<Article> success, Action<Exception> fail)
8: {
9: base.Call<Article>(MethodBase.GetCurrentMethod().Name, new { id }, success, fail);
10: }
11:
12: public void VoteArticle(int id, int vote, Action<int> success, Action<Exception> fail)
13: {
14: base.Call<int>(MethodBase.GetCurrentMethod().Name, new { id, vote }, success, fail);
15: }
16:
17: public void SaveArticle(Article article, Action success, Action<Exception> fail)
18: {
19: base.Call(MethodBase.GetCurrentMethod().Name, new { article }, success, fail);
20: }
21: }
Thanks to the call method you can write a method that is ready to the asynchronous nature of network calls made with windows phone. You can user the generic method when you need to return a value or the base implementation when the method returns void. The first parameter is the name of the method you want to call. If you respect the convention of calling the proxy method with the same name of the service method you can use the MethodBase.GetCurrentMethod() to retrieve the name of the method to call.
Finally it is important you also use the same name for method parameters. When you pass the arguments as an anonymous type, Visual Studio automatically create the properties with the same name as the parameter we pass. Internally the Call method scans these properties and add them to the service call with the right name.
Beat the competitors
The proxy you have created in this brief article is highly reusable, but the same is true if you think at the output of the web service. This let you provide a service that is fully interoperable with all the platforms, but Windows Phone will beat other competitors in term of fast development, using the services in a seamless way. As a side note, please be sure that you can apply the same tecnique to provide a version of the proxy to Silverlight.
Download the source code