WCF and JSON

October 2011 — I need to update this with WCF 4.0.  A number of improvements have been made and I hope to find some time to update this post.

 

I decided that it was finally time to beak down and learn WCF.  Yeah, yeah, I know that you’re thinking that it has been out since 3.0, so who cares.  I didn’t spend any time on the soap aspects, because there’s plenty out there and I’m growing tired and frustrated with SOAP.

I’m interested in REST, JSON and syndication (RSS/ATOM); things that don’t seem to be covered exceptionally well.  I’ve got a ton of links at the bottom and that’s how I learned, stole and copied most of what made my test project work.  If you are using XML, then there’s more choices, but I want to stick with JSON for this.

Here’s the source code: Source Code (zip).  I kept this as simple as possible.

the Microsoft way

I’ll call this “msajax” from here out, since it is ajax; it just has some secret sauce in it.  The secret sauce is all built around hiding the details of doing plain ol’ REST + JSON/XML.

  1. Microsoft added additional metadata to the JSON objects.  Being a good (lazy) developer, I didn’t spend a ton of time digging into it, see the links below for more details.  They add the “d” object, which can cause strangeness with non msajax solutions as well as a ___type parameter.
  2. The msajax approach allows for complex objects to be passed as query params on the URL.  I guess if you want to avoid a post operation and use a “get” operation and pass an entire object as a query param you can.  This doesn’t work for plain-old-rest services, so just avoid this.
  3. You can’t use BodyStyle.Bare with msajax; everything has to be wrapped; duh, see item #1.
  4. You can’t use the “UriTemplate” with msajax, because I suspect is causes issues with the generated ScriptManager code.  Digging into this issue is left as an exercise for  the reader.
  5. I believe items 3 & 4 are really limitations imposed by the msajax components(UpdatePanel & ScriptManager).  I appreciate simplification, but please, please, please stop trying to “protect” developers from important details.  Okay Microsoft, can you do this?

The final thing to make things JSON in a msajax world, you have to specify the “enableWebScript” behavior.  Doing this, you don’t have to specify the request and response format types to be JSON in the operations contract, but I’d still specify them to avoid confusion.

<behavior name="msAjaxBehavior"> <enableWebScript /> </behavior> 

 

the REST of the world way

Now, if you want your REST + JSON services to work how the rest of the world does them, you can.  You just have to know the secret combination of configuration, operations contract and javascript tricks to make it work.  Don’t worry, it is really simple once you download a couple of javascript libraries for the client side and follow a few simple rules on the WCF side.

First, in the behaviors section, use the “webHttp” behavior; that’s intuitive isn’t it?

<behavior name="poxBehavior"> <webHttp /> </behavior>

 

The second thing you do is specify to use either the bare or wrapped body style (BodyStyle = WebMessageBodyStyle.Bare) in the WebInvoke attribute.  All this does is determine whether the JSON looks like (bare):

{ "param" : "value"}

or (wrapped):

{"MethodResult" {"param": "value"}}

The final thing you have to do to make your service REST + JSON is to specify that the request and response formats are JSON.  The big drawback to this is that you can’t have one interface that supports REST + XML and REST + JSON.  I thought about hacking deep into the WCF code and figuring out how to make the serialization configurable, but I decided that I had better things to do, like mow the lawn.  Here’s a complete operations contract:

[OperationContract]
[WebInvoke( Method="POST",
            ResponseFormat=WebMessageFormat.Json,
            RequestFormat=WebMessageFormat.Json,
            BodyStyle=WebMessageBodyStyle.Bare)]
CompositeType GetDataUsingDataContractBare(CompositeType composite);

 

Don’t worry about CompositeType; it is just a class that I stole from an article to test out various members and how they serialized.  Strings, ints, arrays, List<string> all work as you’d expect, but Dates pose a few problems.  Below, there’s more details about dates.

Let’s get to some code!

JQuery & Ajax

Now let’s get to the fun stuff and call the services via JQuery.  Honestly, it can’t get any easier; just use the ajax method and let jQuery handle the headaches!

function ajaxPost(urlStr, compositeType, callback) {
    var jsonData = JSON.stringifyWcf(compositeType);

    $.ajax(
    {
        type: "POST",
        contentType: "application/json;",
        url: urlStr,
        data: jsonData,
        processData: false,
        dataType: "json",
        success: function(msg) {
            callback(msg);
        },
        error: function(xhr, status, error) {
            ajaxError(xhr, status, error);
        }
    });
}

Here’s the sample JSON object used:

var jsonDataWrapped = { composite: { "BoolValue": false,     "StringValue": "a wrapped string", "DateTimeValue": new Date(1968, 2, 9),     "StrArray": ["one", "last"]} };

 

And the method used to call the function:

$('#btn_ajax_wrap').click(function() {
    jsonDataWrapped.BoolValue = false;
    ajaxPost("Service1.svc/json/GetDataUsingDataContractWrapped",               jsonDataWrapped, wrappedResultWcfDate);
});

 

As you can see, there’s not much to calling a WCF service.  jQuery handles most of the complexities, you just have to pass in the right JSON object, set the right URL and let it happen.  Doing a “GET” operation is as simple as changing the type to “GET”.

A few key points:

  1. The wrapped object name has to be the same name as the input parameter name on the operation.  That’s why it is “{ composite: { …”; if the param was named foobar it would have been “{ foobar: {…”.
  2. Always pass something for the “data” parameter or the content-length isn’t set.  See the link for the ugly details; yeah, I got bit by this.
  3. Make sure you set the dataType to “json”
  4. Set processData to false; there’s no reason to process it twice.

It gets even easier

What you didn’t see was all of the ugliness of handling dates, wrapped versus bare and a few other nuisances.  Instead of figuring this out, do yourself a favor and use Rick Strahl’s ServiceProxy javascript library.  Rick has done the hard work and you can thank him by buying his products.  Instead of giving you all of the details, just go here, http://www.west-wind.com/Weblog/posts/896411.aspx, and read it for yourself.  Trust me, it is worth your time.  Here’s the direct link to the code http://www.west-wind.com/weblog/images/200901/ServiceProxy.zip

Config

  <system.serviceModel> <services> <!-- WCF + JSON --> <service behaviorConfiguration="AW_Services.Service1Behavior"             name="AW_Services.Service1"> <endpoint address="json" behaviorConfiguration="poxBehavior"             binding="webHttpBinding" contract="AW_Services.IService1" />  </service> <!-- Microsoft AJAX --> <service behaviorConfiguration="AW_Services.Service2Behavior"              name="AW_Services.Service2"> <endpoint address="msajax" behaviorConfiguration="msAjaxBehavior"              binding="webHttpBinding" contract="AW_Services.IService2" /> <endpoint address="pox" behaviorConfiguration="poxBehavior"              binding="webHttpBinding" contract="AW_Services.IService2" />  </service>  </services> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> <behaviors> <endpointBehaviors> <behavior name="msAjaxBehavior"> <enableWebScript /> </behavior> <behavior name="poxBehavior"> <webHttp /> </behavior> </endpointBehaviors> <serviceBehaviors> <behavior name="AW_Services.Service1Behavior"> <serviceMetadata httpGetEnabled="true" httpGetUrl="" /> <serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true" /> </behavior> <behavior name="AW_Services.Service2Behavior"> <serviceMetadata httpGetEnabled="true" httpGetUrl="" /> <serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true" /> </behavior>  </serviceBehaviors> </behaviors> </system.serviceModel> 

 

Overall, it is a very simple config, but the tags can be a bit funky.  Since WCF doesn’t allow me to specify the serialization format on the query string, I add either “json” or “xml” into the path to differentiate (see endpoint address).  While not as nice as being able to specify http://www.somedomain.org/service.svc?format=xml or format=json, it provides a clean separation.

Dates

Dates and JSON are a big hair ball.  Microsoft has their way, Javascript has their way and there’s an ISO standard.  Like most things that don’t have a standard, there’s plenty of room for issues.  Look at the links below and you’ll understand the issue.  The easiest way to deal with the issue is use the JSON extension that’s included with Rick’s ServiceProxy code.  He did an excellent job of making the date problem a non issue.  HUGE thanks to Rick.

Best Practices

Take these with a big grain of salt; I haven’t done extensive testing and this is just what I was able to make work and what I’m sticking with going forward.  And, remember that if you want to use ms ajax instead of jQuery or other libraries, stick with the “enableWebScript” and let MS handle the ugly details.

  1. Always specify the request & response format (JSON or Xml).  Don’t leave it to the config to runtime to determine.  A lot of you won’t agree with this, but that’s okay.
  2. Use webHttp, so you don’t have to deal with the MS secret sauce
  3. Bare vs Wrapped request & responses is really a choice.  I’m using wrapped, because it really reduces the security issues that exist if you return arrays.  Search on JSON and vulnerabilities; you’ll see what exists.
  4. Don’t try to send entire objects as query params; switch to a post operation and send the JSON object.
  5. Use the UriTemplate to make your paths nicer.  Instead of “Service.svc/GetMyData?key=123”, you can use UriTemplate “mydata/{key}”, so the url is “Service.svc/mydata/123”.  This is just a little more RESTish, but I’m not a purist.
  6. Use JSON, ServiceProxy and JQuery to hide the details.
  7. Use JSON as the wire format.  It is far more compact than XML and you don’t have to convert it to javascript on the client side.  Saves some CPU cycles, transmits faster and is nearly as easy to read as XML.

JSON vs XML

I’m really getting on the JSON bandwagon as a more than just a client side technology.  Using it as the wire format is gaining huge momentum, because it is close to expressiveness as XML, but a WHOLE LOTTA smaller in size.  JSON is also gaining popularity as a storage format.  Check out MongoDB and you’ll see how they are using it.

Links

About these ads

5 Responses to WCF and JSON

  1. rbrentodell says:

    Nice work Ken. I had a hell of a time figuring out WCF a year or so ago but I am a huge fan of it now. I am going to try a few of the things you have implemented here, specifically moving away from XML to JSON which is something I been kicking around for a month or so.

    Keep the great blogs coming.

  2. kwiebke says:

    Once you get past the syntax with squiggly brackets and lots of colons, it is really easier to read than XML. Just think of JSON as a bunch of nested hashtables and it becomes very easy to understand. The best part is that jQuery, JSON.org, Microsoft and others have done the hard work. Also, take a look at MongoDB and some of the other NoSQL DBs. They are using JSON for everything and storing the data in a binary version. We’re just starting to use it a Block, but may use it in a big way this year. If we make it all work, I’ll have to blog about it.

  3. Tomasz says:

    Reaaaaallly nice work!
    It took hours for me to determine where Microsoft have hidden all these nasty details, and – there you go – you have that all given as on a plate! Big thank you!

    • kwiebke says:

      I really need to update this with the 4.0 stuff as MS finally decided to use the accept header to handle XML or JSON.

  4. Rupali says:

    Very nice work Ken.
    I stumbled upon your blog while looking for a solution to my REST + WCF issue. Can you please have a look at : http://stackoverflow.com/questions/11708490/issue-in-passing-object-array-to-restful-wcf-json-service
    and let me know if you find any solution for it please ?

    Thanks

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: