Designing WCF Services for Web 2.0 Clients

Consuming WCF Services from ASP.NET AJAX and Silverlight

Exploring WCF Web Services

LANGUAGES: C#

TECHNOLOGIES: .NET 3.5 | WCF

 

Designing WCF Services for Web 2.0 Clients

Consuming WCF Services from ASP.NET AJAX and Silverlight

 

By Michele Leroux Bustamante

 

This monthly column explores how to design your WCF services to support ASP.NET AJAX and Silverlight clients comparing SOAP, XML, and JSON approaches. If you have questions about migrating your existing ASMX or WSE Web services to WCF, or questions regarding WCF as it relates to Web services, send them to [email protected]!

 

In last month s column, Exposing Classic HTTP Endpoints with WCF in .NET 3.5, I introduced the underlying WCF features released with .NET 3.5 that make it possible to support a Web programming model for plain-old XML (POX), JavaScript Object Notation (JSON), Representational State Transfer (REST), and syndication services such as RSS. The article focused on POX services and introduced new WCF types specific to Web programming, such as WebHttpBinding, WebHttpBehavior, WebServiceHostFactory, WebServiceHost, and WebChannelFactory. This article will build on that foundation and show you how to expose JSON endpoints to simplify service consumption from ASP.NET AJAX and Silverlight clients. I ll introduce additional WCF types to support this model; compare SOAP, XML, and JSON approaches; and show you what the client programming model looks like for each.

 

Web 2.0 Clients

Traditional thick clients such as those created with Windows Forms or Windows Presentation Foundation (WPF) have an advantage over Web 2.0 clients in that they can work with a rich object model to communicate with WCF services. Client developers typically generate a proxy from the service WSDL or through metadata exchange, and use this and its related WCF client configuration to build a communication channel capable of calling a service endpoint using SOAP messaging. With this programming model, clients and services can work with all the rich protocols available with WCF, including those WS* protocols for security, reliable messaging, and transactions.

 

Web 2.0 clients typically rely on JavaScript to communicate with services remotely, thus they don t have access to this rich programming model, nor can they support the breadth of WS* protocols. Using JavaScript, you can call SOAP, XML, or JSON service endpoints, but the choice of protocol plays a role in the relative simplicity of the required client code to call the service. Because a typical service proxy cannot be used by JavaScript, alternate methods of invocation must be used, such as the classic XmlHttpRequest object or special JavaScript proxies that can automatically be generated in some cases.

 

Figure 1 illustrates a WCF service exposing endpoints for SOAP, XML, and JSON consumption by an ASP.NET page. In this example, the client uses XmlHttpRequest directly from page script to call each service. As I ll discuss, the coding effort for this model can be cumbersome.

 


Figure 1: XmlHttpRequest can be used to call SOAP, XML, and JSON services.

 

Figure 2 illustrates an alternate approach for JSON endpoints where an ASP.NET AJAX-enabled page uses a JavaScript proxy to call the service. In this case, the call to XmlHttpRequest is handled by the underlying ASP.NET AJAX libraries, and the ASP.NET ScriptManager control handles generating the proxy dynamically for the client to invoke. The client code simply creates an instance of the proxy to make calls to the service.

 


Figure 2: JSON supports automatically generated JavaScript proxies to call JSON services.

 

Silverlight clients are a special breed, in that they can use traditional JavaScript calls to invoke services, they can use the ScriptManager if you use ASP.NET 3.5 Futures, or they can use .NET code to invoke a service using a special object model that wraps the XmlHttpRequest. Figure 3 summarizes this list of options.

 

Client Technology

Programming Model

Protocol Support

ASP.NET AJAX/JavaScript

XmlHttpRequest

SOAP/XML/JSON

ASP.NET AJAX/ScriptManager

Generated Proxy

JSON

Silverlight/JavaScript

XmlHttpRequest

SOAP/XML/JSON

Silverlight/ScriptManager

Generated Proxy

JSON

Silverlight/.NET Code

BrowserHttpWebRequest

SOAP/XML/JSON

Figure 3: Web 2.0 client options to call WCF services.

 

In the rest of this article, I ll take you on a tour of these programming models as I describe how the WCF service should be designed and configured for each.

 

XmlHttpRequest and SOAP Services

Many services expose only SOAP endpoints, so it is important to be able to use SOAP protocol from any client environment, including Web 2.0 clients. Although cumbersome, it is possible to consume a SOAP service from JavaScript using the XmlHttpRequest object. Consider this simple WCF service contract:

 

[ServiceContract(Namespace = "thatindigogirl.com/samples")]

public interface ISOAPService

{

    [OperationContract]

   string EchoString(string s);

}

 

Assuming the service implementing this contract will be consumed by the XmlHttpRequest object, it would be necessary to provide an endpoint that doesn t rely on addressing any of the WS* protocols thus, BasicHttpBinding is the most appropriate choice, as shown here:

 

 

   contract="ISOAPService" />

 

Figure 4 shows the code to invoke this service using the XmlHttpRequest object.

 

Figure 4: Using XmlHttpRequest to call a SOAP service.

 

The request posts the SOAP message to the target service endpoint; in this case, SOAPService.svc. For the message to be well-formed, the object must first be initialized with the correct headers, indicating the correct content type and SOAP action. Furthermore, the entire envelope must be manually constructed, including namespaces. For this particular message, the job is trivial, but you can imagine the tedium of building a SOAP message that deals in complex XML payloads. You would have to inspect the WSDL for the service and determine how to build the message for each operation. For this reason, SOAP is not the preferred approach for Web 2.0 clients.

 

XmlHttpRequest and XML Services

In last month s column I explained how you can use the new WCF features released with .NET 3.5 to expose POX endpoints. This changes your service contracts in that they must apply the WebGetAttribute for operations that respond to HTTP GET requests, or the WebInvokeAttribute for any HTTP verb including GET, POST, PUT, or DELETE (POST is the default behavior). Using the same attributes, you would also specify WebMessageFormat.Xml so the service channel will process plain XML requests because the default for these attributes initializes the runtime to use JSON. The following example shows how the service operation from the SOAP example earlier would be configured to support XML endpoints:

 

  [ServiceContract(Namespace = "thatindigogirl.com/samples")]

public interface IXmlService

{

    [OperationContract]

    [WebInvoke(RequestFormat = WebMessageFormat.Xml,

     ResponseFormat = WebMessageFormat.Xml,

    BodyStyle = WebMessageBodyStyle.Wrapped)]

   string EchoString(string s);

 // other operations

}

 

To support XML messaging, you must expose an endpoint using WebHttpBinding and enable the WebHttpBehavior:

 

 

   contract="IXmlService"

behaviorConfiguration="webHttpBehavior" />

...

 

   

 

 

WebHttpBinding with WebHttpBehavior enables support for POX, REST, JSON, and other non-SOAP services. Recall from last month that you also can enable a default endpoint and behavior by using the WebServiceHost instead of the default ServiceHost type. This is done by associating the WebServiceHostFactory with your .svc endpoints, as follows:

 

<%@ ServiceHost Service="XmlService" CodeBehind="~/

 App_Code/XmlService.cs"  Factory=

 "System.ServiceModel.Activation.WebServiceHostFactory" %>

You now have a service that can receive XML messages. Calling an XML endpoint using XmlHttpRequest is slightly more straightforward than with a SOAP endpoint, but the tedium of preparing the correct XML request format and parsing the XML response still exists. Figure 5 shows the code to invoke an XML service using XmlHttpRequest.

 

Figure 5: Using XmlHttpRequest to call an XML service.

 

If the contract is configured to use WebMessageBodyStyle.Wrapped, the request must be formatted to include the correct namespace, a wrapper element named for the operation, and any nested parameter elements. The request and response for EchoString then looks like this:

 

// request

hello

 

// response

 XmlService received a string:

 hello

 

If you change the contract to use WebMessageBodyStyle.Bare, a element with the correct namespace must be provided in the request and the response will have a similar format:

 

// request

hello

         

// response

XmlService received a string: hello

 

When you use the XmlHttpRequest object to call an XML service, the code differs from a SOAP service in that the target URL includes the operation name, the XML payload is simplified somewhat (no need to create a compete SOAP message), and the content-type header is application/xml instead of text/xml. The programming model is still cumbersome for both, and still requires knowledge of the appropriate message payload.

 

JSON-enabled Services

JSON provides many benefits to the Web 2.0 clients. The message payload is greatly simplified, and there are many supporting features of the client programming model that naturally work with JSON to streamline the coding effort. Specifically, processing JSON responses from a service is super easy.

 

You can expose JSON endpoints for your WCF services if you define a contract that supports JSON and enable the appropriate channel behaviors to process JSON messages. As with POX services, JSON services are now easily supported by WCF, thanks to the new Web programming features in .NET 3.5 that I discussed last month with a few slightly different switches. To design a service contract supporting JSON messaging, first select WebMessageFormat.Json for the request and reply format for each operation, as shown here:

 

[OperationContract]

[WebInvoke(RequestFormat = WebMessageFormat.Json,

 ResponseFormat = WebMessageFormat.Json, BodyStyle

 = WebMessageBodyStyle.WrappedRequest)]

string EchoString(string s);

 

In fact, WebMessageFormat.Json is the default setting, but it is always a good idea to explicitly declare your usage intentions to avoid confusion. The WebScriptEnablingBehavior also requires WebMessageBodyStyle.WrappedRequest.

 

As with XML endpoints, you use WebHttpBinding with the WebHttpBehavior for JSON endpoints to be consumed by XmlHttpRequest. However, if you want to expose JSON services for streamlined consumption by ASP.NET AJAX clients, you should use the WebScriptEnablingBehavior, shown here:

 

 

   contract="IJsonService"

behaviorConfiguration="webScriptBehavior" />

...

 

   

 

 

This behavior enables a number of JSON-related channel features, such as support for automatic proxy generation. You can automatically enable this behavior by employing the WebScriptServiceHostFactory in your .svc definition removing the need to explicitly associate the endpoint behavior:

 

<%@ ServiceHost Service="JsonServiceScript" CodeBehind="~/App_Code/JsonServiceScript.cs" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>

 

XmlHttpRequest and JSON Services

If you expose a simple JSON endpoint, you can consume it from any AJAX client using the XmlHttpRequest object previously discussed for SOAP and XML. Once again, you are then forced to write code to construct the request object, set the correct URL and content-type, and figure out the correct parameters and resulting JSON format for posting to the service operation. Considering the JSON service contract in the previous section, a call to EchoString would look like this:

 

request = new XMLHttpRequest( );

request.open ("POST", "JsonServiceWeb.svc/EchoString", true);

request.onreadystatechange = OnEchoStringComplete;

var params= '{"s":"hello"}';

request.setRequestHeader("Content-type", "application/json");

request.send(params);

 

JSON is certainly much simpler, not to mention shorter, than formatting SOAP or XML payloads but the effort to create the XmlHttpRequest is still necessary to figure out operation types for parameters and return values. Fortunately, you can reduce this code to a single line if you leverage the WebScriptEnablingBehavior alongside the ASP.NET ScriptManager control.

 

JSON Proxies

After you enable the WebScriptEnablingBehavior, a magical thing happens to your service endpoint: You now can browse to the endpoint, append a /js suffix, and, presto, you have a JavaScript proxy that encapsulates calls to XmlHttpRequest to reach the service endpoint. For example, if you browse to the following JsonServiceWebScript.svc endpoint, you ll be prompted to open the proxy file or save it to disk:

 

http://localhost/... /JsonServiceWebScript.svc/js

 

Save the file with the extension .js and you can inspect it to see what the proxy looks like in JavaScript. The proxy exposes functions that match the signature of your service operations. I m not going to dissect the proxy here, but you ll need to know the namespace that was generated from your service contract in order to use the proxy. For example, the service contract above uses this namespace:

 

[ServiceContract(Namespace = "thatindigogirl.com/samples")]

 

This results in the following JavaScript namespace and proxy name (in the form of a JavaScript function):

 

Type.registerNamespace('thatindigogirl.com.samples');

thatindigogirl.com.samples.IjsonServiceScript

 =function() {...}

 

That means, to call EchoString using the proxy, you would use this syntax:

 

thatindigogirl.com.samples.IJsonServiceScript.EchoString(

  hello );

 

This proxy relies on the ASP.NET AJAX JavaScript libraries to execute the XmlHttpRequest under the covers. Rather than saving the generated proxy and including it in your page by hand, the ASP.NET ScriptManager control handles including the necessary script libraries and generating the proxy for any service references you provide it. For example, to reference JsonServiceWebScript.svc, you would provide the following service reference:

 

 

 

 

 

When the page is parsed, the ScriptManager will inject a script reference such as this into your page:

 

 

Now when you write code inside the page to invoke service operations, you can use the proxy directly, and you even get IntelliSense! This makes it incredibly easy to invoke JSON services using a familiar object model for each operation.

 

The first parameters to each operation will be those defined by the service. The proxy also adds three additional parameters that you can pass to the call:

  • Callback Handler: A JavaScript function to be called with the service response so you can process the response.
  • Error Handler: A JavaScript function to be called in the event an exception occurs.
  • State: Any state information that would be useful to the callback.

 

For example, when you call EchoString, the code, and handlers, might look like this:

 

function OnEchoString()

{

 thatindigogirl.com.samples.IJsonServiceScript.EchoString(

  "hello", OnEchoStringComplete, OnError);

}

function OnEchoStringComplete()

{

 $get("stringResult").innerText = "EchoString() called

  with result: " + result;

}

function OnError(error, userContext, methodName)

{

 $get("stringResult").innerText = "Error calling " +

   methodName + ": " + error.get_message();

}

 

Silverlight Clients

Silverlight clients can call services in three ways:

  • Using XmlHttpRequest from JavaScript. This is applicable when you generate pages that have an associated JavaScript file such as Page.xaml and Page.xaml.js.
  • Using ScriptManager if you install ASP.NET 3.5 Futures and are working with JavaScript.
  • Using the BrowserHttpWebRequest type provided to Silverlight, which wraps the XmlHttpRequest object for code-behind calls.

 

The last option implies that you create a Silverlight page that uses code-behind instead of JavaScript. You can create new Silverlight pages with code-behind from a Silverlight project (as opposed to a Silverlight Web site). You can then reference the Silverlight project from a Web site using the new Add Silverlight Link menu from the project node in Solution Explorer (see Figure 6). This copies the XAML files from the referenced project, and copies the binary (with the code-behind files) to the \ClientBin directory beneath the Web site keeping those files up-to-date as you recompile the solution with changes.

 


Figure 6: From a Web site, adding a link to a Silverlight project.

 

In the code-behind you can write code to communicate with services using the BrowserHttpWebRequest object. This represents a slightly different object model to do the same thing you would with XmlHttpRequest which means you could call SOAP, XML, or JSON services. JSON is the more likely scenario for Web 2.0, so I ll use that as an example here.

 

Figure 7 shows the required code to invoke a JSON service from a Silverlight code-behind file using BrowserHttpWebRequest. This object model isn t dissimilar to XmlHttpRequest, in that you must provide the Uri for the service operation, set the content-type and length, and populate the outgoing request stream with the JSON-formatted content. Likewise, to process the response you grab the response stream and read the content to populate UI if applicable.

 

string serviceAddress = HtmlPage.DocumentUri.AbsoluteUri;

System.Uri operationUri = new System.Uri(serviceAddress.Substring(0,

serviceAddress.IndexOf("/Default.aspx")) + "/JsonService.svc/EchoString");

BrowserHttpWebRequest request = new BrowserHttpWebRequest(operationUri);

string content = "{\"s\":\"hello\"}";

request.Method = "POST";

request.ContentType = "application/json";

request.ContentLength = content.Length;

      

HttpWebResponse response = null;

using (StreamWriter writer = new StreamWriter(request.GetRequestStream()))

{

 writer.Write(content);

 writer.Flush();

 response = request.GetResponse();

}

if (response.StatusCode != HttpStatusCode.OK)

{

 string s = response.StatusDescription;

}

else

{

 using (StreamReader reader = new StreamReader(response.GetResponseStream()))

 {

   string s = reader.ReadToEnd();

   s = s;

 }

}

Figure 7: Using BrowserHttpWebRequest to call a JSON service.

 

Conclusion

This article provides a crash course in Web 2.0 options for consuming your WCF services. Now you know which client technologies can call SOAP, XML, or JSON services, which programming model you can use for each, and how to configure your services to support each model. Clearly, the best possible option is to use the automatically generated JavaScript proxy for ASP.NET AJAX clients, including Silverlight, because it provides a friendly object model with significantly less code required. The code samples associated with this article illustrate each example and provide you with the remaining context for each scenario so be sure to look at those as well! Enjoy!

 

Download the samples for this article at http://www.dasblonde.net/downloads/aspproapr08.zip.

 

Michele Leroux Bustamante is Chief Architect of IDesign Inc., Microsoft Regional Director for San Diego, and Microsoft MVP for Connected Systems. At IDesign, Michele provides training, mentoring, and high-end architecture consulting services focusing on Web services, scalable and secure architecture design for .NET, federated security scenarios, Web services, interoperability, and globalization architecture. She is a member of the International .NET Speakers Association (INETA), a frequent conference presenter, conference chair for SD West, and is frequently published in several major technology journals. Michele also is on the board of directors for IASA (International Association of Software Architects), and a Program Advisor to UCSD Extension. Her latest book is Learning WCF (O Reilly, 2007); see her book blog at mailto:www.thatindigogirl.com. Reach her at mailto:[email protected], or visit http://www.idesign.net and her main blog, http://www.dasblonde.net.

 

Additional Resources

Learning WCF, O Reilly 2007: http://www.thatindigogirl.com (get all the book code here!)

Michele s blog: http://www.dasblonde.net

IDesign: http://www.idesign.net

WCF Home: http://wcf.netfx3.com

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish