Creating JSON-enabled WCF services in .NET 3.5

Just over a year ago, I wrote an article for MSDN Magazine detailing how to make client-side web service calls with the ASP.NET Ajax extensions to .asmx web services. While most of the contents of that article still apply today to .NET 3.5 and Visual Studio 2008, there is a fundamental shift going on away from .asmx and towards .svc (WCF) for web services, so I thought it would be timely to post an update to that article to describe how to use WCF for your script-callable web services in .NET 3.5.

With .asmx Ajax services, you create a class in a .asmx file (or associated code behind file), attribute it with the [ScriptService] attribute, reference the .asmx endpoint in the Services section of your ScriptManager control on your .aspx page, and you're off and running. The new WCF Ajax-enabled service support works fundamentally the same way: parameters and results are serialized using a JSON serializer, JavaScript proxy classes are automatically generated when you reference the .svc endpoint with a /js at the end of the URL, and the invocation mechanism on the client stays the same using the familiar asynchronous callback model developers have become accustomed to with .asmx script services. The actual details of creating and referencing the service in Visual Studio 2008, however, is quite different from how .asmx script services work, and because the .asmx script service model is still in place and available, I have seen quite a few developers stick with .asmx instead of shifting their services over to .svc endpoints for familiarity. It's fine if you want to stick with .asmx endpoints, but the web service story at Microsoft is all about WCF these days, and will continue to be so in the future, so if you have already adopted the 3.5 .NET runtime, I highly recommend migrating your script web services over to the WCF model. Which brings me to the main point of this post – there has been a fair amount of confusion about how to create script-enabled WCF web services throughout the betas of 3.5, and a search on the web bring up many misleading results. My goal with this post is to provide very specific and easy to follow instructions on how to create script enabled WCF services with the final release of Visual Studio 2008 and .NET 3.5.

The simplest way to create an Ajax-enabled WCF endpoint is to use the new Visual Studio 2008 Ajax-enabled WCF Service item template. You can also script-enable an existing WCF service, but to keep this streamlined, we'll start with this template.

If you add a new service named WeatherService using this template, it will do three things for you:

  1. Create a new WeatherService.svc file that will serve as the endpoint.
  2. Create a WeatherService.cs code behind file with a class named WeatherService and a method named DoWork() ready to be implemented (or removed).
  3. Modify your web.config file to include a <system.serviceModel> section describing your new endpoint.

Let's start by removing the DoWork() method in WeatherService.cs and adding in a more appropriate GetForecast() method.

[ServiceContract(Namespace = "")]

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

public class WeatherService

{

static Random _rand = new Random();

[OperationContract]

public string GetForecast(string zip)

{

switch (_rand.Next(3))

{

case 0:

return "Sunny and warm";

case 1:

return "Cold and rainy";

case 2:

return "Windy with a chance of snow";

default:

return "Invalid";

}

}

}

Now we're ready to call the service from script, so add a ScriptManager to the page you want to make the service call from, and add a ServiceReference to the Services element:

<asp:ScriptManager ID="ScriptManager1" runat="server">

<Services>

<asp:ServiceReference Path="~/WeatherService.svc" />

</Services>

</asp:ScriptManager>

Then wire up some client-side action to initiate the call – here's a sample script and piece of HTML that invokes our service:

<script type="text/javascript">

function OnGetForecast()

{

WeatherService.GetForecast($get("zip").value, OnGetForecastComplete, OnError);

}

function OnGetForecastComplete(result)

{

$get("weatherResult").innerText = result;

}

function OnError(result)

{

alert(result.get_message());

}

</script>

Enter zip: <input type="text" id="zip" />

<input type="button" value="get forecast" onclick="OnGetForecast()" /><br />

<span id="weatherResult"></span>

So far, it feels pretty much the same, no? Things begin to feel a bit different if you begin making other changes, however. For example, let's specify a real namespace in our service definition (which was defaulted to an empty string).

[ServiceContract(Namespace = "http://www.pluralsight.com/ws/")]

This actually affects the client-side script proxy that is created – it will now be in the www.pluralsight.com.ws namespace, so we need to adjust our client script accordingly:

function OnGetForecast()

{

www.pluralsight.com.ws.WeatherService.GetForecast($get("zip").value, OnGetForecastComplete, OnError);

}

Note this is quite different from the way .asmx script services worked – there the class namespace was used in the client proxy, not the web service namespace. In .svc script services, the client proxy is always encapsulated in the web service namespace and the namespace of the implementation class never enters the picture. This was rather confusing in earlier releases of the WCF Ajax-enabled template which left the ServiceContract unadorned with a namespace, which meant that the web service lived in the default tempuri.org namespace. In order to reference your web services from the client side proxy, you would have to type tempuri.org.WeatherService… even though the string tempuri.org didn't show up anywhere in your code! (Keep this in mind if you ever see a script-enabled WCF service with an empty ServiceContract attribute).

Ok, the next difference you're bound to run into is also related to namespaces. If you encapsulate your web service class in a namespace (C# or VB namespace this time), you will need to make changes in both the .svc file as well as your web.config file to accommodate the name change. Let's try encapsulating our class in a namespace:

namespace Pluralsight

{

[ServiceContract(Namespace = "http://www.pluralsight.com/ws/")]

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

public class WeatherService …

}

Now we modify the WeatherService.svc file to reflect the new class (just like we would have if it were a .asmx file):

<%@ ServiceHost Language="C#"

Debug="true"

Service="Pluralsight.WeatherService"

CodeBehind="~/App_Code/WeatherService.cs" %>

And finally, we need to make two changes to the web.config which also references the class in the endpoint description:

<system.serviceModel>

...

<services>

<service name="Pluralsight.WeatherService">

<endpoint address="" behaviorConfiguration="WeatherServiceAspNetAjaxBehavior"

binding="webHttpBinding" contract="Pluralsight.WeatherService" />

</service>

</services>

</system.serviceModel>

The one last thing you might find yourself missing as you migrate from .asmx-based script services to .svc script services, is the handy test page that you see when you access the .asmx endpoint directly from the browser. In fact, if you point a browser to our WeatherService.svc file, you will see a notification that "Metadata publishing for this service is currently disabled", along with a lengthy description of the configuration elements necessary to enable it. Unfortunately there is no auto-generated POST-based test page for WCF services, so you're probably best just using the JavaScript proxy to try invoking the methods as a test. There is a test client available (WcfTestClient.exe) but it is designed to work with a service that has been compiled into an assembly, so it is not easily used with the App_Code model of an ASP.NET Web site.

If you are using Web Application Projects instead of the Web site model, you will have an assembly to test your service from if you like, which may be the topic of a future post...


Posted Jan 31 2008, 02:03 PM by fritz-onion
Filed under:

Comments

Ryan wrote re: Creating JSON-enabled WCF services in .NET 3.5
on 01-31-2008 1:19 PM
Nice post. Here is another tip that a lot of people don't know about: You can ditch all that web.config WCF configuration mumbo-jumbo if you simply change the Factory on the svc file. Flip it to this:

Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory"

This is how we support a config-less service using JSON in WCF. It is meant to be as easy as using ASMX was (no config). However, I am not sure why we didn't update the VS project templates to show this method.

You can then get rid of the code-behind attribute in the svc file as well.
Fritz Onion wrote re: Creating JSON-enabled WCF services in .NET 3.5
on 01-31-2008 4:09 PM
Ah, I remember hearing about that switch now that you mention it - thanks Ryan!
-Fritz
John wrote re: Creating JSON-enabled WCF services in .NET 3.5
on 02-12-2008 10:44 AM
This isn't working in Firefox for me. Is anyone else having the same problem?
siva wrote re: Creating JSON-enabled WCF services in .NET 3.5
on 02-25-2008 1:44 AM
Nice post
Mr.Y wrote re: Creating JSON-enabled WCF services in .NET 3.5
on 02-28-2008 12:33 AM
It's not working in Firefox becuase Firefox doesn't support - "innerText".
Just change it to "InnerHTML" and everything will be just fine ;-)
Rama wrote re: Creating JSON-enabled WCF services in .NET 3.5
on 03-21-2008 2:24 PM
I get an JavaScript Error - 'Access is Denied'. The WCF is running not in the same virtual directory as the website though.
RST wrote re: Creating JSON-enabled WCF services in .NET 3.5
on 04-24-2008 12:31 AM
trying to host a REST enabled WCF service on IIS 5.0 throuw the following error: "IIS specified authentication schemes 'IntegratedWindowsAuthentication, Anonymous', but the binding only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, or Anonymous. Change the IIS settings so that only a single authentication scheme is used." If i remove the windows authentication i get 404 not found, so - am i missing something? if this cannot run on IIS than i will not be able to deploy it. Please help!!! I tried using the WebServiceHostFactory in the svc file, and also tries using only configuration, and even tried to use a custom factory that adds the webHttpBinding and behavior on the endpoint by code. none of them works. (Self hosting is working, the problem is when this is hosted on IIS). is this related to IIS 5.0? I tries both VS2005 and VS 2008 RTM - some problem.
Joe Pretontius wrote re: Creating JSON-enabled WCF services in .NET 3.5
on 05-07-2008 11:50 AM

Perhaps someone can help me with the following.

I altered the above example so that the weather forecast is returned not as a string, but an instance of an added class, Forecast, which has two public properties, ExpectedWeather (string) and WhenForecast (the DateTime at which the weather was predicted).

I added the DataContract attribute to the Forecast class and reran the sample. The returned result is [Object object]. I then added the DataMember attribute to the public properties, but after doing so, I get a runtime error indicating that the proxy object is invalid.

Take out what I believe to be a required attribute and the sample runs without being able to access properties of the returned object. Add the attribute back in and I've got no proxy.

Please help...
WEC wrote re: Creating JSON-enabled WCF services in .NET 3.5
on 05-13-2008 12:47 PM
I implemented the above example using Visual Web Developer Express 2008 and while it worked great using the local debugger, it does not seem to work as a standalone IIS application. Am I missing something? Others in the office have had the same problem and they are theorizing it having something to do with how Express Edition compiles these web sites versus the usual web project found in the standard/prof editions of Visual Studio. While I known express isn't meant for the development of enterprise/production applications, we wer hoping to showcase some of the new JSON related stuff without having to run it locally. Any thoughts?

Add a Comment

(required)  
(optional)
(required)  
Remember Me?