Contact   •   Products   •   Search

Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs

WCF and JSON Services


I took a quick look today at WCF's new capability to work with JSON data. I was looking forward to having WCF services providing this functionality and there are a few cool things that work with it. It's now essentially possible to create WCF Web Services that take JSON as input and produce JSON output and it's pretty easy to do in fact.

For example I can create a Svc style service in an ASP.NET application like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using TimeTrakker;
using System.Data;
using System.Web.Script.Services;
using System.ServiceModel.Web;

namespace TimeTrakkerWeb
{
    [ServiceContract( Namespace="", Name="TimeTrakkerService")]
    public interface ITimeTrakkerService
    {
        [OperationContract]
        //[WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest, 
        //           ResponseFormat = WebMessageFormat.Json, 
        //           UriTemplate = "helloworld/{name}")]
        string Helloworld(string name);

        [OperationContract]
        [WebInvoke(BodyStyle = WebMessageBodyStyle.WrappedRequest,
                ResponseFormat = WebMessageFormat.Json,
                UriTemplate = "loadcustomer")]
        //[WebGet(BodyStyle = WebMessageBodyStyle.WrappedRequest,
        //        ResponseFormat = WebMessageFormat.Json,
        //        UriTemplate = "loadcustomerGet/{Pk}")] 
        CustomerEntity LoadCustomer(string Pk);

    }


    public class TimeTrakkerService : ITimeTrakkerService
    {
        public string Helloworld(string name)
        {
            return "Hello World, " + name;
        }

        public CustomerEntity LoadCustomer(string Pk)
        {
            busCustomer Customer = new busCustomer();
            Customer.Options.ThrowExceptions = true;

            CustomerEntity cust = Customer.Load(int.Parse(Pk));

            cust.InvoiceEntities = null;
            cust.ProjectEntities = null;            

            return cust;
        }
    }
}

And the following in Web.config to configure the service:

<system.serviceModel>
  <services>
    <service name="TimeTrakkerWeb.TimeTrakkerService" 
behaviorConfiguration
="TimeTrakkerServiceBehavior">
      <!--<endpoint contract="TimeTrakkerWeb.ITimeTrakkerService" binding="basicHttpBinding"/>-->
      <endpoint contract="TimeTrakkerWeb.ITimeTrakkerService" 
binding="webHttpBinding"
behaviorConfiguration="AjaxBehavior"
/>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name="TimeTrakkerServiceBehavior">
        <serviceDebug includeExceptionDetailInFaults="true"/>
        <serviceMetadata httpGetEnabled="true"/>          
      </behavior>
    </serviceBehaviors>
    <endpointBehaviors>
    <behavior name="AjaxBehavior">          
        <enableWebScript  />          
      </behavior>
    </endpointBehaviors>
  </behaviors>
</system.serviceModel>

The key features to enable JSON access are the behavior configuration in the configuration file that adds the enableWebScript behavior and the additional attributes on each of the service method that should be exposed to JSON. Actually the attributes are required only if you explicitly need to use REST operations - the default behavior allows you to use POST based (ASMX style) JSON requests against the server where the input is a JSON parameter list.

That's all nice and neat if you're using MS Ajax. Unfortunately it appears that everything about this service uses the funky MS AJAX formatting for JSON result sets and inputs. For example the above result returns the following JSON string:

{"d":{"__type":"CustomerEntity:#TimeTrakker", "Pk":1,"UserId":"0 ","LastName":"Strahl", "FirstName":"Rick","Company":"West Wind Technologies","Address":"32 Kaiea Place","City":"Paia", "State":"HI ","Zip":"96779 ","Zip4":" ","Country":"USA", "CountryId":"US ","Phone":"(503) 914-6335", "Email":"rstradhl@west-wind.com","Fax":"1\/1\/1900","Notes":"", "Entered":"\/Date(1187593200000-0700)\/","Updated":"\/Date(1191366660000-0700)\/", "LastOrder":"\/Date(-2208960000000-0800)\/", "BillingRate":150.00,"Xml":null, "tversion":[0,0,0,0,0,0,184,67],"ProjectEntities":[],"InvoiceEntities":[]}}

Basically the result is wrapped in an additional type (presumably to allow for multiple results?) and there's a __type property attached to each object returned. Even simple result values at least include the wrapper type, so even say a string result includes the d: type.

If you're using MS AJAX on the client there's no issue - the client knows how to parse this JSON into a clean object. But if you're using some other mechanism - jQuery or Prototype for example to retrieve a result set you get back a funky object where you have to go result.d.LastName for example, which is ugly.

There are a few options on the WebGet (and WebInvoke) attribute that allow specifying the BodyStyle and one of the options there is BodyStyle.Bare, but unfortunately it doesn't work with any of these AJAX responses apparently.

That's a real shame too, because the WebGet and WebInvoke methods allow for a really nice REST model for accessing requests by using UrlTemplates. Notice the:

UriTemplate = "loadcustomer/{pk}")

in the attributes above which allows you to specify a parameter as part of the URL extension. So to retrieve a particular customer in this case you'd use a URL like this:

localhost/timetrakkerweb/services/TimeTrakkerService.svc/LoadCustomer/1

Multiple parameters can be specified in this same format. With a little URL Rewriting you can get some decent looking URLs to access your data fairly easily.

So what would client code (non MS Ajax look like to call this service?) Even with the funky JSON object results the code is still pretty trivial. Here I'm using my wwScriptLibrary (from wwHoverPanel) for the ajax callback:

<script type="text/javascript">
function CallService()
{
    ajaxJson("services/timetrakkerservice.svc/LoadCustomer",{"Pk":"1"},Callback,onError);
}
function Callback(result)
{
    alert(result); // string
    alert( result.d.Company);
    alert( result.d.ProjectEntities[1].CustomerEntity.LastName);
}
function onError(error)
{
    alert("Error: " + error.message);
}

In this case I'm using the 'POST' mechanism to post back the result to the server. MS Ajax expects an object with a property for each of the parameters provided to the method which in this case is a single string parm of the customer id. I chose the string parm here because when using the GET mechanism WCF failed to convert the URL based 1 to an int on its own:

Operation 'LoadCustomer' in contract 'TimeTrakkerService' has a path variable named 'Pk' which does not have type 'string'. &nbsp;Variables for UriTemplate path segments must have type 'string'.

That's pretty lame given that simple values at least could easily be translated. However, typically it's better to use POST anyway.

If you're using MS Ajax you can add a script manager script reference to the service:

        <asp:ScriptManager ID="ScriptManager1" runat="server">
        <Scripts>
            <asp:ScriptReference Path="services/TimeTrakkerService.svc/js" />
        </Scripts>
        </asp:ScriptManager>

which adds the script proxy to the page and you can then directly call the Web Service by its fully qualified name (ie. Namespace.Interface.Class by default). Normally that'd be something like tempuri.org.ITimeTrakkerService.TimeTrakkerService, but I explicitly set the namespace of the ServiceContract to none and the name to just to TimeTrakkerService. So the service call is as simple as:

function CallService()
{
    TimeTrakkerService.LoadCustomer("1",Callback);
}
function Callback(result)
{
    alert(result);
    alert( result.Company);
    alert( result.ProjectEntities[1].CustomerEntity.LastName);
}

A bit cleaner of course.

Playing around a bit with the options there are a number of ways that services can be accessed using GET, POST and PUT. This is all controlled via attributes (WebGet, WebInvoke etc.) I found another annoying issue though:  It appears you can't specify both WebGet and WebInvoke for the same service method. When you do you get:

Operation 'LoadCustomer' in contract 'TimeTrakkerService' has both WebGetAttribute and WebInvokeAttribute; only one can be present.

It's quite likely that you might want to expose both Get and Post behavior to a service, although POST is usually the preferred mechanism. Certainly in public access REST scenarios you'd want to give people access in a variety of ways.

On the other hand I was pleasantly surprised that WCF WAS able to handle a circular reference scenario with LINQ to SQL entities (which is what the above returns) nicely. It cut off the circular referencing nicely and appropriately returned me the list of child items.

All in all it's nice to see JSON support in WCF, but I'm not sure how well this will really play outside of MS AJAX. Given that REST services are supposed to be as lean as possible and easy to consume the issue of the extra type wrapper is kind of a show stopper. I hope that there is somehow a way to get WCF to produce clean JSON instead of this proprietary format.

Make Donation
Posted in AJAX  Microsoft AJAX  WCF  


Feedback for this Post

 
# re: WCF and JSON Services
by Matt November 02, 2007 @ 11:04pm
Hi Rick,
I'm trying to make the most simple service that consumes and output xml. I followed http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2282952&SiteID=1
and am able to output xml properly, but after almost a day, I am not any closer to consuming xml.

Here is what my contract looks like.

[ServiceContract]
public interface ITestService
{
[OperationContract, WebInvoke(
BodyStyle = WebMessageBodyStyle.Bare,
RequestFormat = WebMessageFormat.Xml,
ResponseFormat = WebMessageFormat.Xml)]
XmlElement GetAsXml(Stream input);
}

I am using fiddler to simulate a client. Here is what I am sending using fiddler:
POST http://127.0.0.1:60000/TestService.svc/GetAsXml HTTP/1.1
User-Agent: Fiddler
Host: 127.0.0.1:60000
Content-Length: 204

<TextBlock x:Name="text" IsHitTestVisible="false" Text="Hello" Foreground="black" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"/>

The response I get it is HTTP/1.1 415 Unsupported Media Type

If I add Content-Type: application/xml; charset=utf-8 to the request headers, I get the following response:
HTTP/1.1 400 Bad Request

Any ideas. All I want to do is parse the xml that is in the post request on the server
# re: WCF and JSON Services
by Rick Strahl November 02, 2007 @ 11:25pm
Not sure. I haven't had any luck with the bare requests. But I think your problem is that you're not sending the proper content type. I'd guess you need to add Content-type: text/xml
# re: WCF and JSON Services
by Dan Finch November 09, 2007 @ 1:14pm
try WebMessageBodyStyle.Bare
# re: WCF and JSON Services
by Rick Strahl November 09, 2007 @ 2:38pm
I tried all of the various message formats, but none of them seemed to work. Some would just fail outright with exceptions and others still used the MS Ajax format.
# re: WCF and JSON Services
by Vish January 12, 2008 @ 6:26pm
Have you tried using the WebHttpBehavior instead of WebScriptBehavior? Check out http://hyperthink.net/blog/2007/08/23/WebHttpBehavior+Vs+WebScriptBehavior.aspx
# re: WCF and JSON Services
by Rick Strahl January 12, 2008 @ 7:34pm
Yes - this stuff works correctly in RTM. Those configuration behaviors and the factories effectively do the same thing with the behaviors being more configurable if needed. I'll have more on this in a bit.
# re: WCF and JSON Services
by Sean May 07, 2008 @ 7:28pm
Hey Rick, I attended this years dev connections 2008 and I'm actually trying to throw together a presentation for my group at work based on the seminars I attended, your WCF/REST/JSON class being one of them. I beleive our app could benefit from these technologies so I've been putting together a power point and some demo apps... but for the life of me I cant get the WCF service reference working for some reason in my demo web site code, which is calling a fairly simple WCF (Json output) service.

Is there any way you could post a simple zipped up project including a really simple service / web site calling it, I imagine I'm doing something stupid .. but then again its almost 11pm and a couple captain and cokes later.

Thanks much either way,

Sean
# re: WCF and JSON Services
by Sean May 07, 2008 @ 9:05pm
Whoops, ignore my last comment, just found your web log link to the dev connections sample code.

- Sean
# re: WCF and JSON Services
by Sean May 07, 2008 @ 9:11pm
DOH! I have the same problem w/ your code... I get a client script error saying "AjaxService is undefined" I'm using studio 2008 team server on vista.

This concludes my web log comment spamming for the evening.

Any ideas?

Thanks

Sean
# re: WCF and JSON Services
by Mahesh A December 02, 2008 @ 5:44am
Please try

WebOperationContext.Current.OutgoingRequest.ContentType
# re: WCF and JSON Services
by Hellen February 11, 2009 @ 12:54pm
I am getting this error.

Endpoints using 'UriTemplate' cannot be used with 'System.ServiceModel.Description.WebScriptEnablingBehavior'.
# re: WCF and JSON Services
by Rick Strahl February 11, 2009 @ 1:25pm
@Hellen - Right. When you’re running in AJAX mode you can’t use UriTemplate(). Either remove the UriTemplate or remove the enableWebScript behavior from the configuration.

This post is a bit more detailed on configuration:
http://www.west-wind.com/WebLog/posts/310747.aspx
# re: WCF and JSON Services
by phucnd March 12, 2009 @ 8:36pm
Have you got a demo project?
Please send me?
# One way to work around...
by Sunny April 22, 2009 @ 6:07am
Perhaphs instead of using:
result.d.Company

You could:
result = get_json(result);
var name = result.Company;

function get_json(result){
return result.d;
}

so that the item is isolated & does not spread throughout your code...

My 2 cents ;)
# re: WCF and JSON Services
by Rick Strahl April 22, 2009 @ 2:04pm
@Sunny - yes of course. You'd want to create a wrapper for the JSON calls anyway because there are several issues you need to deal with in the JSON handling and error trapping and reporting which is vital.

There's a service wrapper client class that provides this functionality here:
http://www.west-wind.com/weblog/posts/324917.aspx
# re: WCF and JSON Services
by rpiz November 17, 2009 @ 1:55am
Hi..
I got error..

- using TimeTrakker; (could not be found)
- CustomerEntity could not be found

The first error..
is it should be the "TimeTrakker" is refer to the "namespace TimeTrakkerWeb"
So, if I change to "TimeTrakkerWeb" it ok...
The 2nd error I dont have any idea. Pls advice..
Thanks
-Json_Newbie-
# re: WCF and JSON Services
by Ricl O'Shay December 14, 2009 @ 8:43pm
JAX-RS (and RESTEasy in JBoss or Jersey in Glassfish) provide a nice design example of how to expose REST services using any and all of the HTTP verbs in an almost effortless manner. You map a path prefix in web.config to point to the base service, then you add classes that append their own sub-resource (e.g., "exchange" in this case) and then each method has its own sub-resource. So you might say "GET /rest/exchange/yen?amount=500". The HTTP verb, query parameters, path parameters, and everything else is easily specified using a dirt simple annotation. Every MIME type is supported, coming and going (I show strings here but all the mime types are in an enumeration, you really use the enumerated value). If only Microsoft would do this!!!

@Produces("Text")
@Consumes("Text")
@Path("/exchange")
public interface CurrencyExchange
{
@GET
@Path("/yen")
public Decimal toYen(@QueryParam("amount") Decimal amt);
...
@POST
@Path("/rates")
@Produces("JSON")
public Rates getRates(@FormParam("date") date);
}

It's an absolute joy to use. No muck, no mire, just simplicity.
# re: WCF and JSON Services
by Jeremy Wood April 15, 2010 @ 12:09pm
Had the same issue removing <enableWebScript /> from the endpoint behavior gets rid of it.
# re: WCF and JSON Services
by Akhil Gupta July 07, 2010 @ 12:00am
Hi i m using the following code -
<system.serviceModel>
<services>
<service name="AkhilWcfService.Service" behaviorConfiguration="ServiceBehavior">
<!-- Service Endpoints -->
<endpoint contract="AkhilWcfService.IService"
binding="webHttpBinding"
behaviorConfiguration="AjaxBehavior" />

</service>

</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceDebug includeExceptionDetailInFaults="true"/>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->

</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="AjaxBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Data;
using System.Web.Script.Services;
using System.ServiceModel.Web;

namespace AkhilWcfService
{
// NOTE: If you change the interface name "IService" here, you must also update the reference to "IService" in Web.config.
[ServiceContract(Namespace = "", Name = "Service")]
public interface IService
{
[OperationContract]
string Helloworld(string name);

}

}
-------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using AkhilWcfService;


public class Service : IService
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}


public string Helloworld(string name)
{
return "Hello World, " + name;
}

}
--------------------------------------------------------
& i m getting error as
Service 'Service' has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.
I don't know where is the problem
Please please help me.
I m looking for this heavily & from a long time.
# re: WCF and JSON Services
by Maulik October 08, 2010 @ 10:38am
Have you ever tried invoking HTTP GET service (which is web script enabled) from AJAX? Whenever I try to do that, AJAX still invokes it as POST service.
# re: WCF and JSON Services
by Rick Strahl October 08, 2010 @ 10:52am
@Maulik - sure you can send an AJAX request without POST. Just use the correct options on your AJAX client. With jQuery $.ajax() make sure method is GET.
# re: WCF and JSON Services
by Maulik October 08, 2010 @ 12:09pm
Hey Rick,

Thanks for looking into this!!!

But I an using Javascript and <ScriptReference> like this:

  <asp:ScriptReference Path="http://localhost/RestFormat/wcf/MAPClass_WebScript.svc/rest/js" />


and then invoke it like this:

MAP._2010.Services.MAPClass_WebScript.IMAPClass2010_WebScript.WebScriptTest_WebScript('1234', 'xyz', onSuccess, onError);


When I see the log on server side. I found that it is always sending HTTP POST.

Does it make sense?
# re: WCF and JSON Services
by Rick Strahl October 08, 2010 @ 2:26pm
Script references always use POST. All the MS AJAX client stuff does when talking to a service - they can't make plain REST calls (at least not the service oriented stuff).

Why would you need to? If you have REST calls use jQuery (or whatever other EASY HTTP client you want to use) to return the data and skip the Web Service interfaces.
# re: WCF and JSON Services
by Maulik October 08, 2010 @ 3:16pm
Thanks Rick!!

Basically I am not the consumer of the service. I am providing infrastructure for hosting web services. I was trying to test HTTP GET web script enabled service (hosted in my application) from an AJAX client proxy and I ran into this problem.

I think because we are hosting web services, we cannot make any assumptions about how the consumers are going to use these services. That is the reason I am debating with myself whether I should support GET for web script enabled services or not.

What would be the best course of action according to you?
# re: WCF and JSON Services
by Rick Strahl October 08, 2010 @ 3:25pm
You can implement dual interfaces - just create two endpoints one for POST and one for GET. Unfortunately WCF doesn't allow a single URL to handle this - you need separate URLs for this.

Frankly I wouldn't use WCF for that sort of thing. HTTP Handlers or even one of the other REST toolkits (or West Wind AJAX & Web Toolkit) which provide more flexibility on how data can be accessed off a specific URL. For example, in the WW Toolkit any service end points can be accessed via POST or GET.
# re: WCF and JSON Services
by Vaishali January 06, 2011 @ 8:39am
How are you using <enableWebScript> behaviour along with URITemplate. I am getting error if I do so "Requested Service cannot be activated" and the cause being "Endpoints using 'UriTemplate' cannot be used with 'System.ServiceModel.Description.WebScriptEnablingBehavior'"
# re: WCF and JSON Services
by Rick Strahl January 07, 2011 @ 12:12pm
@Vaishali - you can't use the <enableWebScript> with any of the Http WCF attributes. WebScript implies MS AJAX style messaging which REQUIRES that message inputs are passed as JSON (with each parameter an object property) and via POST. In that case you only markup your methods with [OperationContract].

If you want raw REST style endpoints use the <webHttp /> key instead of <enableWebScript />.

                <webHttp defaultBodyStyle="Bare"
                          defaultOutgoingResponseFormat="Json"
                          faultExceptionEnabled="true"
                          helpEnabled ="true" />
# re: WCF and JSON Services
by Shabir Hakim February 09, 2011 @ 10:58pm
You are simply Good.You do always something different...

Regards
# re: WCF and JSON Services
by Peter McClymont January 10, 2012 @ 1:24pm
Hi, Thanks for the info.

I found a problem when I tried to implement this, I got this message in fiddler,

415 Cannot process the message because the content type 'application/json; charset=UTF-8' was not the expected type 'text/xml; charset=utf-8'.

I found the solution here,

http://meronymy.blogspot.com/2011/05/fixing-wcf-json-applicationjson-not.html

In the svc file I needed this,

Factory="System.ServiceModel.Activation.WebServiceHostFactory"

Did you need that for your solution?
 


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2014