Enabling JSONP in WCF

4/23/2010

Before this week I never really bothered much with WCF. My reasons were two fold, one it always seemed like a pain to configure and the second reason was the fact that it was overkill for the projects that I was working on. That being said, I finally hit a project where it made sense to use it. I needed things like the ability to easily do JSON and XML requests, access through more than just a web request, etc. Anyway, I set myself up with a new project space, followed a couple tutorials, figured out how to host the services in a web app separate from my other apps so that the services would be centralized, etc. Basically everything was going fine... Well until I tried to make my requests from web app 1 (the project that needed access to the service) to web app 2 (the app actually holding the service). It kept coming back with a failure message.

How could this be? I set up my service contract correctly, my function even had the necessary WebGet attribute:

   1: [WebGet(ResponseFormat=WebMessageFormat.Json)]

That one attribute attached to my function should set it up for JSON. I had my service reference set up in my script manager. I even had the WCF Service file set up correctly using the WebScriptServiceHostFactory:

   1: <%@ ServiceHost Service="Services.MyService" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>

According to the documentation I wouldn't even need to bother with configuring the config file. I could strip out the system.serviceModel portion and that factory should handle things for me. Hell if I called it in the browser, it passed back the JSON correctly so what the hell was going on? JSONP... That's right, in all of my haste I had completely forgotten about JSONP (JSON with Padding). JSONP, in a very basic description, is a usage pattern that allows a secondary server (not the primary server) to use JSON in a meaningful way. In otherwords think cross site scripting, which is exactly what I was trying to do. My calls from Sys.Net.WebServiceProxy.invoke (long story as to why I'm not able to simply use jQuery) were requesting JSONP to be sent back and were failing when it didn't receive it. So I went searching online as to how to enable it in WCF.

Apparently I'm the only person out there who has decided to work with .Net 4.0 on anything and especially WCF. Well either that or no one wanted to talk about it. Anyway, after about 2 hours of digging, I finally found some semblance of help on this subject. You see prior to 4.0, the way you were suppose to set up JSONP was that you had to define a number of classes including a message encoder, a message encoder factory, an operationbehaviorattribute, etc. It was a pain and every post about it said just wait until 4 comes out as it will have it by default... Well it does:

   1: <?xml version="1.0"?>
   2: <configuration>
   3:  
   4:   <system.web>
   5:     <compilation debug="false" targetFramework="4.0" />
   6:   </system.web>
   7:   <system.webServer>
   8:     <modules runAllManagedModulesForAllRequests="true"/>
   9:   </system.webServer>
  10:   <system.serviceModel>
  11:     <behaviors>
  12:       <endpointBehaviors>
  13:         <behavior name="webHttpBehavior">
  14:           <webHttp />
  15:         </behavior>
  16:       </endpointBehaviors>
  17:     </behaviors>
  18:     <bindings>
  19:       <webHttpBinding>
  20:         <binding name="webHttpBindingJsonP" crossDomainScriptAccessEnabled="true"></binding>
  21:       </webHttpBinding>
  22:     </bindings>
  23:     <services>
  24:       <service name="Services.MyService">
  25:         <clear />
  26:         <endpoint address="" binding="webHttpBinding" bindingConfiguration="webHttpBindingJsonP"
  27:                   contract="Services.Interfaces.IMyService" behaviorConfiguration="webHttpBehavior" />
  28:         <host>
  29:           <baseAddresses>
  30:             <add baseAddress="http://SERVICELOCATION" />
  31:           </baseAddresses>
  32:         </host>
  33:       </service>
  34:     </services>
  35:   </system.serviceModel>
  36: </configuration>

The bit above is the web.config file for the service web app. It turns out the whole Factory attribute in the svc file is kind of lie, you do indeed need the system.serviceModel section. Anyway, in it we define a endpoint behavior for webHttpBehavior and a binding that uses webHttpBinding called webHttpBindingWithJsonP. The special thing about the binding is the crossDomainScriptAccessEnabled attribute is set to true. This one item enables it. Past that we just set up our service and an endpoint using the binding that we've created and that's it (and of course set up the base address). Two hours and that's all I needed to do. Anyway, I hope it helps someone out there so you don't have to deal with the headache that I've had today. So give it a try, leave feedback, and happy coding.



Comments

BLop
February 18, 2012 4:26 AM
Just to inform :"Cross domain javascript callback is not supported in authenticated services" while using crossDomainScriptAccessEnabled="true"

James Craig
May 21, 2010 10:17 AM

It would depend on whether or not you're using .Net 4 or something earlier. The JSONPBehavior class is from the WCF samples, so my assumption is you're using something earlier. If that's the case what I have above wont help you much as it's focused on .Net 4. I would recommend looking at this post to get it working. http://jasonkelly.net/2009/05/using-jquery-jsonp-for-cross-domain-ajax-with-wcf-services/If you're using .Net 4 on the other hand, you can leave off the JSONPBehavior. Instead in the system.serviceModel section of your config, you need to create a binding configuration that sets crossDomainScriptAccessEnabled to true and then set up an end point using that binding configuration.

waltdjr
May 21, 2010 8:24 AM

I'm also trying to do the same thing except I am using jquery for mine. Do you still have to add the <JSONPBehavior(callback:="callback")> to the contract like this:<OperationContract()> _<WebGet(ResponseFormat:=WebMessageFormat.Json)> _<JSONPBehavior(callback:="Callback")> _The reason I ask, is when I try to add it, I get JSONPBehavior is not defined. However, if I don't add it, I don't get the JSONP wrapper returned. If I do have to add it, do you know what I have to import? I've tried servicemodel, servicemodel.web, servicemodel.security and servicemodel.activation.Any suggestions or guidance would be greatly appreciated.Thanks in advance,WaltDjr