Understanding WCF Services in Silverlight 2
Sunday, November 16, 2008
Contents
- Introduction
- Service Setup In Depth
- Client Access
- Security
- Handling Exceptions
- JSON Service Connectivity
- Thread Waiting
- Using DevServer
- Conclusion
Introduction
One of the most beautiful things about the Windows Communication Foundation (WCF) is that it's a completely streamlined technology. When you can provide solutions to myriad of diverse problems using the same principles, you know you're dealing with a work of genius. This is the case with WCF. With a single service implementation, you can provide access to ASMX, PHP, Java, TCP, named pipe, and JSON-based services by add a single XML element for each type of connection you want to support. On the flip side, with a single WCF client you can connect to each of these types of services, again, by adding a single like of XML for each. It's that simple and streamlined. Not only that, this client scenario works the same for both .NET and Silverlight.
In this discussion, we are going to talk about how to access WCF services using Silverlight 2 without magic. There will be no proxies, no generated code, and no 3rd party utilities. Just raw WCF. This discussion will cover WCF connectivity in quite some depth. We will talk about service setup, various WCF and Silverlight paradigms, client setup, some security issues, and a few supplemental features and techniques to help you aide and optimize service access. You will learn about various WCF attributes, some interfaces, and a bunch of internals. Though this discussion will be in depth, nothing will ever surpass the depth of MSDN. So, for a more full discussion on any topic, see the WCF documentation on MSDN.
Even though we're focusing on Silverlight, most of what will be explained will be discussed in a .NET context and then applied to Silverlight 2. That is, instead of learning .NET WCF and Silverlight WCF, you will .NET WCF and how to vary this for Silverlight. This comparative learning method should help you both remember and understand the concepts better. Before we begin, though, let's begin with a certain WCF service setup. After all, we you don't have a service, we can't talk about accessing it.
Service Setup In Depth
When working with WCF, you are working with a completely streamlined system. The most fundamental concept in this system is the ABC. This concept scales from Hello World to the most complex sales processing system. That is, for all WCF communication, you need an address, a binding, and a contract. Actually, this is for any communication anywhere, even when talking to another person. You have to know to whom, how, and what. If you don't have these three, then there can't be any communication.
With these three pieces of information, you either create a service-side endpoint which a client will access or a client-side channel which the client will use to communicate with the service.
WCF services are setup using a 3 step method:
- First, create a service contract with one or more operation contracts.
- Second, create a service implementation for those contracts.
- Third, configure a service host to provide that implementation with an endpoint for that specific contract.
Let's begin by defining a service contract. This is just a simple .NET interface with the System.ServiceModel.ServiceContractAttribute attribute applied to it. This interface will contain various operation contracts, which are simply method signatures with the System.ServiceModel.OperationContractAttribute applied to each. Both of these attributes are in the System.ServiceModel assembly. Here's the sample that we will use for the duration of this discussion:
using System; using System.ServiceModel; //+ namespace Contact.Service { [ServiceContract(Namespace = Information.Namespace.Contact)] public interface IPersonService { //- GetPersonData -// [OperationContract] Person GetPersonData(String personGuid); } }
The Namespace property set on the attribute specified the namespace used to logically organize services. Much like how .NET uses namespaces to separate various classes, structs, and interfaces, SOAP services use namespaces to separate various actions. The name space may be arbitrarily chosen, but the client and service must just agree on this namespace. In this case, the namespace is the URI http://www.netfxharmonics.com/service/Contact/2008/11/. This namespace will also be on the client. This isn't a physical URL (universal resource locator), but a logical URI (universal resource identifier). Despite what some may say, both terms are in active use in daily life. Neither is more important than the other and neither is "deprecated". All URLs are URIs, but not all URIs are URLs as you can see here.
Notice in this interface, there is a method interface that returns Person. This is a data contract. Data contracts are classes which have the System.Runtime.Serialization.DataContractAttribute attribute applied to them. These have one or more data members, which are public or private properties or fields that have the System.Runtime.Serialization.DataMemberAttribute attribute applied to them. Both of these attributes are in the System.Runtime.Serialization assembly. This is important to remember; if you forget you will probably assume them to be in the System.ServiceModel assembly and your contract will never compile.
Notice I said that data members are private or public properties or fields. That was not a typo. Unlike the serializer for the System.SerializableAttribute attribute, the serializer for DataContract attribute allows you to have private data members. This allows you to hide information from developers, but allow services to see it. Related to this is the how classes with the DataContract attribute differ from classes with the Serializable attribute. When you use the Serializable attribute, you are using an opt-out model. This means that when the attribute is applied to the class, each members is serializable. You then opt-out particular fields (not properties; thus one major inflexibility) using the System.NonSerializedAttribute attribute. On the other hand, when you apply the DataContract attribute, you are using an opt-in model. Thus, when you apply this attribute, you must opt-in each field or property you wish to be serialized by applying the DataMember attribute.
Next, in step 2, we need to use these contracts to create an implementation. The service implementation is just a class which implements a service contract. The service implementation for our discussion here is actually incredibly simple:
using System; //+ namespace Contact.Service { public class PersonService : Contact.Service.IPersonService { //- @GetPersonData -// public Person GetPersonData(String personGuid) { return new Person { FirstName = "John", LastName = "Doe", City = "Unknown", Guid = personGuid, PostalCode = "66062", State = "KS" }; } } }
That's it. So, if you already have some logic you know is architecturally sound and you would like to turn it into a service. Create an interface for your class and add some attributes to the interface. That's your entire service implementation.
Step 3 is to configure a service host with the appropriate endpoints. In our discussion, we are going to be using an HTTP based service. Thus after we setup a new web site, we create a Person.svc file in the root and add to it a service directive specifying our service implementation. Here's the entire Person.svc file:
<%@ ServiceHost Service="Contact.Service.PersonService" %>
This is an unconfigured service host. Thus, we must configure it. We will do this in the service web site's web.config file. There's really only one step to this, but that one step has a prerequisite. The step is this: setup a service endpoint, but this requires a declared service. Thus, we will declare a service and add it to an endpoint. An endpoint specifies the WCF ABC: an address (where), a binding (how), and a contract (what). Below is the entire web.config file up to this point:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.serviceModel> <services> <service name="Contact.Service.PersonService"> <endpoint address="" binding="basicHttpBinding" contract="Contact.Service.IPersonService" /> </service> </services> </system.serviceModel> </configuration>
This states that there can be "basicHttpBinding" communication through Contact.Service.IPersonService at address Person.svc to Contact.Service.PersonService. Let's quickly cover each concept here.
The specified address is a relative address. This means that the value of this attribute is appended onto the base address. In this case the base address is the address specified by our web server. In the case of a service outside of a web server, then you can specify an absolute address here. But, remember, when using a web server, the web server is going to control the IP address and port bindings. Our service is at Person.svc, thus we have already provided for us the base URL. In this case the address is blank, but you will use this address attribute if you add more endpoints as you will see later.
The binding specifies how the information is to be format for transfer. There's actually nothing too magical about a binding, though. It's really just a collection of binding elements and pre-configured parameter defaults, which are easily changed in configuration. Each binding element will have at a minimum two binding elements. One of these is a message encoding binding element, which will specify how the message is formatted. For example, the message could be text (via the TextMessageEncodingBindingElement class; note: binding elements are in the System.ServiceModel.Channels namespace), binary (via the BinaryMessageEncodingBindingElement class), or some other encoding. The other required binding element is the transport binding element, which specified how the message is to go over the wire. For example, the message could go over HTTP (via the HttpTransportBindingElement), HTTPS (via the HttpsTransportBindingElement), TCP (via the TcpTransportBindingElement), or even a bunch of others. A binding may also have other binding elements to add more features. I'll mention this again later, when we actually use a binding.
The last part of an endpoint, the contract, has already been discussed earlier. One thing that you really need to remember about this though is that you are communication through a contract to the hosted service. If you are familiar with interface based development in .NET or COM, then you already have a strong understanding of what this means. However, let's review.
If a class implements an interface, you can access the instantiated object through the interface. For example, in the following code, you are able to access the Dude object through the ISpeak interface:
interface ISpeak { void Speak(String text); } class Dude : ISpeak { public void Speak(String text) { //+ speak text } } public class Program { public void Run() { ISpeak dude = new Dude(); dude.Speak("Hello"); } }
You can think of accessing a WCF service as being exactly like that. You can even push the comparison even further. Say the Dude class implemented IEat as well. Then we can access the instantiated Dude object through the IEat interface. Here's what I mean:
interface ISpeak { void Speak(String text); } interface IEat { void Eat(String nameOfFood); } class Dude : ISpeak, IEat { public void Speak(String text) { //+ speak text } } public class Program { public void Run() { IEat dude = new Dude(); dude.Eat("Pizza"); } }
In the same way, when configuring a WCF service, you will add an endpoint for contract through which you would like your service to be accessed.
Though it's beyond the scope of this discussion, WCF also allows you to version contracts. Perhaps you added or removed a parameter from your contract. Unless you want to break all the clients accessing the service, you must keep the old contract applied to your service (read: keep the old interface on the service class) and keep the old endpoint running by setting up a parallel endpoint.
You will add a new service endpoint every time you change your version, change your contract, or change your binding. On a given service, you have have dozens of endpoints. This is good thing. Perhaps you provide for four different bindings with two of them having two separate configurations each, three different contracts, and 2 different versions of one of the contracts. In this discussion, we are going to start out with one endpoint and add more later.
Now we have setup a complete service. However, it's an incredibly simple service setup, thus not requiring too much architectural attention. When you work with WCF in a real project, you will want to organize your WCF infrastructure to be a bit more architecturally friendly. In my article entitled Creating Streamlined, Simplified, yet Scalable WCF Connectivity, I explain streamlining and simplifying WCF connectivity and how you can use a private/public project model to simplify your WCF connectivity. See that article for more information on streamlining WCF services.
Service Access Without Magic
Now we may turn our attention to the client application. To begin, let me start off by reminding everyone that you shouldn't rely on "Add Service Reference" in Visual Studio for magical service client creation. Frankly, the code is incredibly verbose, hard to manageable, edits are prone to being overwritten, and is almost always used as an excuse to not actually learn WCF. This is all true for .NET and much more so in Silverlight. There's a reason Juval Lowy, in all his books and talks, repeatedly tells people to avoid using it. Fortunately, as professionals, we have the ability to understand how to do things without magic.
As I've mentioned many times already, WCF relies on the concept of the ABC. We've seen how we configure a WCF host by creating an endpoint specifying an address, binding and contract. As it turns out, this is all that's required on the client side as well. For both .NET and Silverlight, you merge an address and a binding with a contract in a channel factory to create a channel. This isn't just fancy conceptual architect speak, this is exactly what your code would look like (the sign of really good architecture!) Below is the .NET version of what I mean:
BasicHttpBinding basicHttpBinding = new BasicHttpBinding(); EndpointAddress endpointAddress = new EndpointAddress("http://localhost:1003/Person.svc"); IPersonService personService = new ChannelFactory<IPersonService>(basicHttpBinding, endpointAddress).CreateChannel(); //+ Person person = personService.GetPersonData("F488D20B-FC27-4631-9FB9-83AF616AB5A6");
In this you have an address (the EndpointAddress object), a binding (BasicHttpBinding), and a contract (IPersonService) meeting in System.ServiceModel.ChannelFactory<TServiceContract> to create a channel which implements the IPersonService interface (and others). We can then call through our local interface to the channel, which then calls the remote service through the service interface. No magic is required. You don't need any "proxy" or anything. WCF is literally all about the ABC on the service and on the client.
A few moments ago I mentioned that a binding is nothing more than a series of binding elements. At this time I would like to prove this to you. In the above sample, we are using a BasicHttpBinding on the client to communicate to a BasicHttpBinding on the service. This is slightly "magical". Let's peel back the covers to see what's really going on. The BasicHttpBinding is the WCF implementation of the WS-I Basic Profile standard used by ASMX services and most PHP clients. In other words, it's the basic SOAP binding. Internally though, it's really nothing more than a combination of the TextMessageEncodingBindingElement and, depending on your settings, either HttpTransportBindingElement or HttpsTransportBindingElement with the message version set to SOAP 1.1.
To prove this, let's completely remove the BasicHttpBinding from our client. Then let's create an instance of the System.ServiceModel.Channels.CustomBinding class. This class allows us to make our own bindings from our own or preexisting binding elements. In this case, all we want to do is add the TextMessageEncodingBindingElement, setting the message version to SOAP 1.1 and add the HttpTransportBindingElement. With this, we have creating a binding that fits exactly what our service expects. Here's what we're talking about:
CustomBinding customBinding = new CustomBinding(); customBinding.Elements.Add(new TextMessageEncodingBindingElement { MessageVersion = MessageVersion.Soap11 }); customBinding.Elements.Add(new HttpTransportBindingElement()); EndpointAddress endpointAddress = new EndpointAddress("http://localhost:1003/Person.svc"); //+ IPersonService personService = new ChannelFactory<IPersonService>(customBinding, endpointAddress).CreateChannel();
If you think that's wild, you will be in shock when you realize that you don't even need to use code to create a custom binding. Let's completely remove the CustomBinding and address from our code and just do the exact same thing in web.config. Here is our new code:
IPersonService personService = new ChannelFactory<IPersonService>("PersonServiceCustomHttpBinding").CreateChannel();
Doesn't get much simpler than that. Now take a look at our web.config:
<system.serviceModel> <bindings> <customBinding> <binding name="HttpTextCustomBinding"> <textMessageEncoding messageVersion="Soap11" /> <httpTransport/> </binding> </customBinding> </bindings> <client> <endpoint name="PersonServiceCustomHttpBinding" bindingConfiguration="HttpTextCustomBinding" address="http://localhost:1003/Person.svc" binding="customBinding" contract="Contact.Service.IPersonService" /> </client> </system.serviceModel>
Believe it or not, our service still works perfectly.
Up to this point we have been discussing WCF in general, it's now time to move towards Silverlight. The ABC and channel concepts still apply, but, since we are in the world of the web, there's one difference in how we access the services from our clients: we are dealing with asynchronous calls.
The Async Pattern
If you are familiar with the concepts of AJAX calls, then you should already be familiar with what I'm talking about. To put it simply, instead of calling a "do" method, you basically set a callback and call "beginDo". Then, instead of obtaining a response from "beginDo", we wait for a callback from the service. Think of it like phone tag between two people who never answer their phone. Person A calls person B and says "call me back at X". Then Person B calls Person A back at X; you don't sit there on someone's voicemail waiting for them to pick up (...like we did in the days of answering machines.)
When this idea is brought to the .NET/Silverlight world, we have something called the Async Pattern, which states that asynchronous methods are to follow the following pattern:
public IAsyncResult BeginOperation(/* <parameters> */, AsyncCallback callback, Object state) public /* <return> */ EndOperation(IAsyncResult result)
If you're never seen this before, then, it's high time you become deeply familiar with it. This pattern is used all over the place in myriad of different contexts. You can use MSDN to find various examples involving this pattern, or just keep reading to see how it's used to get the general idea. If you are going to take Silverlight seriously, then you absolutely must master this pattern.
Not only that, but you must also be very proficient with asynchronous call chains. This is when you call one async operation, wait for the callback, then that callback begins another async operation, whose callback may call another async operation. You just chain the async calls down the line to have a steady, deterministic program flow. This, of course, requires knowledge of the Async Pattern to which we now return our focus.
When working with in Silverlight, this Async Pattern is the type of call pattern that you will be using for just about all service access with or without WCF. For example, there will be no GetPersonData method which returns a Person object. Instead, there will be a BeginGetPersonData method which returns an IAsyncResult object (which you may or may not use). Then, you wait for a callback, which has an IAsyncResult again which you use to obtain a result from EndGetPersonData. This actually means that our service contract in Silverlight will look slightly different than our service contract for .NET. Here's our Silverlight version:
using System; using System.ServiceModel; //+ namespace Contact.Service { [ServiceContract(Namespace = Information.Namespace.Contact)] public interface IPersonService { //- BeginGetPersonData -// [OperationContract(AsyncPattern = true)] IAsyncResult BeginGetPersonData(String personGuid, AsyncCallback callback, Object state); //- LoadPerson -// Person EndGetPersonData(IAsyncResult result); } }
Notice that we are now following the Async Pattern and that the OperationContract attribute has declaratively stated that we are following the pattern via the AsyncPattern property. We may now rewrite our original .NET service-call code to follow the new paradigms and service contract to allow Silverlight interaction. Here's our new code:
BasicHttpBinding basicHttpBinding = new BasicHttpBinding(); EndpointAddress endpointAddress = new EndpointAddress("http://localhost:1003/Person.svc"); IPersonService personService = new ChannelFactory<IPersonService>(basicHttpBinding, endpointAddress).CreateChannel(); //+ AsyncCallback asyncCallBack = delegate(IAsyncResult result) { Person person = ((IPersonService)result.AsyncState).EndGetPersonData(result); this.Dispatcher.BeginInvoke(delegate { spMain.Children.Add(new TextBlock { Text = person.FirstName }); }); }; personService.BeginGetPersonData("F488D20B-FC27-4631-9FB9-83AF616AB5A6", asyncCallBack, personService);
Notice the first three lines are identical. After this, not much remains the same. Instead of doing a simple service-call with a synchronous response, we must set a callback and begin a request. In this particular example, the asyncCallBack object is being given an anonymous method to the BeginGetPersonData method. This anonymous method block will be called when the service calls returns a response back.
Take note of the third parameter in the BeginGetPersonData. Per the Async Pattern, this is "state". This is similar to the Tag object found in VB and in WPF. You can use this to place just about anything you want to send back to the callback. In this case, since we're it's an anonymous method, it's optional, but if the callback were a named method, then the callback may obtain that state by pulling the AsyncState property from the IAsyncResult object passed to the callback. In this example, I'm sending the channel itself as the state so that I can access the channel from the callback. This is important because this is how I'm able to call the EndGetPersonData method and retrieve the service operation response data, a Person object in this case.
Notice also also that the callback has a call to Dispatcher.BeginInvoke. You must do this if you are on a visual element and want to access visual elements in your callback. This will grant you access to the UI thread. Without this, an exception will be thrown. In this case we are adding a TextBlock to a StackPanel. For the duration of this discussion, the following method will be used to write our output to the StackPanel:
//- $Write -// private void Write(String text) { this.Dispatcher.BeginInvoke(delegate { spMain.Children.Add(new TextBlock { Text = text }); }); }
External Web Site Access
Having said all that, if you were to try and run the code I sent to you based on our current service configuration, nothing would actually work. Why's that? Because in our original service-setup the service is hosted in a different web site than the web site itself. The service web site does nothing but WCF hosting, while our application web site hosts our Silverlight application. This basically allows you to keep all your entities highly cohesive and reusable. However, in this case, we are running up against a security wall. By default, you cannot use Silverlight to access a web site which differs by domain, IP address, or port number.
Fortunately, there's an easy way around this: you just need to add a ClientAccessPolicy.xml file in the service root. Here's a sample ClientAccessPolicy.xml file:
<?xml version="1.0" encoding="utf-8"?> <access-policy> <cross-domain-access> <policy> <allow-from> <domain uri="*"/> </allow-from> <grant-to> <resource path="/" include-subpaths="true"/> </grant-to> </policy> </cross-domain-access> </access-policy>
This particular example basically allows everyone everywhere to access the service, granting access to everything. This may be what you want or, perhaps, you want to lock it down a bit by placing a URI of the format http://www.tempuri.org/ in place of the *. Perhaps you even want to add multiple domain elements or, on the other end of the spectrum, restrict access to only a particular service path.
Now while this file looks all well and good, our Silverlight service call still won't work. Why in the world not? Because for WCF in Silverlight to call a service outside of the local web site, you must also specify what headers to allow. In our case, we want to allow the SOAPAction header. The SOAPAction header is what a SOAP service uses to specify what action to run on the service.
Here's our new policy:
<?xml version="1.0" encoding="utf-8"?> <access-policy> <cross-domain-access> <policy> <allow-from http-request-headers="SOAPAction"> <domain uri="*"/> </allow-from> <grant-to> <resource path="/" include-subpaths="true"/> </grant-to> </policy> </cross-domain-access> </access-policy>
Now our WCF Silverlight service will work flawlessly.
For all the information you would ever need on this file, see Network Security Access Restrictions in Silverlight 2 on MSDN.
Configuration Based Access
Our service client call at this point is fully programmatic. This is nice, but perhaps you would like to utilize WCF configuration instead of creating Binding and Endpoint elements manually. This is a great idea, but how in the world do we access configuration in Silverlight when Silverlight is on the client-side, running in a web browser? As it turns out, Silverlight actually does include a configuration file, which gets embedded in to the final XAP file, the ServiceReferences.ClientConfig file.
To utilize this configuration feature, we first need to create the file and add it to our Silverlight project. Then, we must set its build action to Content. In Visual Studio 2008, you do this in the properties window for that file. This will force the file to be zipped in with the rest of the contents of the XAP file, which, if you didn't know, is really nothing more than a ZIP file readable by any ZIP client, much like Word 2007's DOCX format.
At this point, you may just copy/paste the configuration from a .NET WCF client to this Silverlight configuration file. Here's basically what it will look like at this point:
<configuration> <system.serviceModel> <client> <endpoint address="http://localhost:1003/Person.svc" binding="basicHttpBinding" contract="Contact.Service.IPersonService" name="BasicHttpBinding_IPersonService" /> </client> </system.serviceModel> </configuration>
At this point can change our code to use the configuration instead of the created binding and endpoint address objects:
IPersonService personService = new ChannelFactory<IPersonService>("BasicHttpBinding_IPersonService").CreateChannel(); //+ AsyncCallback asyncCallBack = delegate(IAsyncResult result) { Person person = ((IPersonService)result.AsyncState).EndGetPersonData(result); Write(person.FirstName); }; personService.BeginGetPersonData("F488D20B-FC27-4631-9FB9-83AF616AB5A6", asyncCallBack, personService);
Now, once again, while this looks nice, when you compile and run the Silverlight application you will notice that it doesn't even pretend to work. Why? For some reason, you actually need to declare a basicHttpBinding element in the configuration file. Here's the new configuration file:
<configuration> <system.serviceModel> <bindings> <basicHttpBinding /> </bindings> <client> <endpoint address="http://localhost:1003/Person.svc" binding="basicHttpBinding" contract="Contact.Service.IPersonService" name="BasicHttpBinding_IPersonService" /> </client> </system.serviceModel> </configuration>
At this point, the Silverlight client will have full connectivity with the service.
Strongly Typed Client
At this point we could stop. The thing works. Mission Accomplished. However, when designing software, you want to make sure people call fall into success. In this case, a developer might forget how to create a WCF channel. Even then, system internals have nothing to do with the core competency of what's going on. Business developers need to be working on providing a business solution, not writing WCF interaction code. Thus, it's often a good idea to hide the internals of something before anyone else uses it. Even I, as a non-business developer, who love internals and gets great benefits from their understanding, prefer not to see internals in my every day development. Therefore, I normally hide the internal mechanics of the WCF channel by creating a client abstraction.
As with WCF in .NET, you may use the System.ServiceModel.ClientBase<T> class to create an abstraction of the internal WCF channel mechanics. However, due to the Async Pattern, using ClientBase in Silverlight is drastically different how I described it's usage for .NET in "Creating Streamlined, Simplified, yet Scalable WCF Connectivity". Creating a concrete ClientBase class is still fairly straightforward though.
The first thing we need to do is create a class that inherits from ClientBase<IYourServiceContract> where IYourServiceContract is, well, your service contract. However, unless you want a very complicated class, do not implement the IYourServiceContract on this ClientBase like you would normally do in .NET. Instead, for each operation in your service contract, create a method in this class. Don't worry about following the Async Pattern. Our point in creating this abstraction is to hide the internals mechanics, not to simply rearrange them. Having said that, you still need to have a callback parameter if your method returns a response.
In addition to this, make sure your method names make sense. By convention, you should suffix each operation method in your ClientBase with "Async". Then, inside the method, call your method prefixed with "Begin" on the channel created by the ClientBase class. Totally confused? Here's what I mean:
//- @GetPersonDataAsync -// public void GetPersonDataAsync(String personGuid, EventHandler<ClientEventArgs> callback) { Channel.BeginGetPersonData(personGuid, GetPersonDataCallback, callback); }
In reality, all you're doing is taking a call and passing it to the Channel with different parameters. Essentially you are hiding the Async Pattern used by this channel method with something that's more natural to developers. In this channel call, the second parameter, GetPersonDataCallback, is the WCF callback which is called when the actual service call is complete and the third parameter is the state for the Async Pattern. Here's the GetPersonDataCallback method:
//- $GetPersonDataCallback -// private void GetPersonDataCallback(IAsyncResult result) { EventHandler<ClientEventArgs> callBack = result.AsyncState as EventHandler<ClientEventArgs>; if (callBack != null) { callBack(this, new ClientEventArgs { Object = EndGetPersonData(result) }); } }
Since we set the callback as the state in the service call, it follows that we can pull that out and use it to send the response data back to the calling method using the standard .NET event pattern. In this case, the ClientEventArgs class is a simple EventArgs class which holds the response object:
public class ClientEventArgs : EventArgs { //- @Object -// public Object Object { get; set; } }
Now notice that when we are populating this newly created ClientEventArgs, we are setting it's Object property to the result of the EndGetPersonData, which is the second of the two Async Pattern components. It's implementation is extremely simple:
//- $EndGetPersonData -// private Person EndGetPersonData(IAsyncResult result) { return Channel.EndGetPersonData(result); }
Notice also that our ClientBase callback and End methods are private, thus completely concealing the internal WCF mechanics from developers.
There's at least one more method we need to add to our class for it to be complete: the constructors. Personally, I like to add a constructor which accepts a System.ServiceModel.Channels.Binding object and a System.ServiceModel.EndpointAddress object as well as an overload which accepts a string representing the endpoint configuration name. These constructors basically do nothing but bridge to the base class:
//- @Ctor -// public PersonClient(String endpointConfigurationName) : base(endpointConfigurationName) { } public PersonClient(System.ServiceModel.Channels.Binding binding, EndpointAddress address) : base(binding, address) { }
Thus, we have successfully created a ClientBase class to abstract the internal mechanics for developers. To sum up, here's the complete class:
using System; using System.ServiceModel; //+ namespace SilverlightClient { public class PersonClient : ClientBase<Contact.Service.IPersonService> { //- @Ctor -// public PersonClient(String endpointConfigurationName) : base(endpointConfigurationName) { } public PersonClient(System.ServiceModel.Channels.Binding binding, EndpointAddress address) : base(binding, address) { } //+ //- @GetPersonDataAsync -// public void GetPersonDataAsync(String personGuid, EventHandler<ClientEventArgs> callback) { Channel.BeginGetPersonData(personGuid, GetPersonDataCallback, callback); } //- $GetPersonDataCallback -// private void GetPersonDataCallback(IAsyncResult result) { EventHandler<ClientEventArgs> callBack = result.AsyncState as EventHandler<ClientEventArgs>; if (callBack != null) { callBack(this, new ClientEventArgs { Object = EndGetPersonData(result) }); } } //- $EndGetPersonData -// private Contact.Service.Person EndGetPersonData(IAsyncResult result) { return Channel.EndGetPersonData(result); } } }
If you need to add more operations, and you most definitely will, all you need to do is copy/paste this pattern and do a which rename of a new things here and there. You could create a Visual Studio code snippet to setup this pattern as well. Using this class is now as simple as the following:
PersonClient personClient = new PersonClient("BasicHttpBinding_IPersonService"); personClient.GetPersonDataAsync("F488D20B-FC27-4631-9FB9-83AF616AB5A6", OnServiceCallback);
Then, of course, there's the callback method:
//- $OnServiceCallback -// private void OnServiceCallback(Object sender, ClientEventArgs ea) { Person person = ea.Object as Person; if (person == null) { return; } //+ Write(person.FirstName); }
As simple as this is, though, you could actually keep the simplicity of this usage and still upgrade the class to be a bit more general. For this reason, I created my ObjectClient<TServiceContract> class, which is now part of my Themelia for Silverlight product (to be released in the future; see my .NET Themelia Framework here). Here's an example of using it:
ObjectClient<IPersonService> objectClient = new ObjectClient<IPersonService>("BasicHttpBinding_IPersonService"); objectClient.Begin("GetPersonData", OnObjectServiceCallback, null, "F488D20B-FC27-4631-9FB9-83AF616AB5A6");
Then, here's the service callback:
//- $OnObjectServiceCallback -// private void OnObjectServiceCallback(Object sender, ObjectClient<IPersonService>.ClientEventArgs ea) { Person person = ea.LoadResult<Person>(); Write(person.LastName); }
The mechanics of this class follow the same idea as the previous ClientBase, but this one does not use ClientBase, but, rather, creates the channel itself. Then, when the "Begin" method is called, the class internally passes the parameters to the proper "BeginMethodName" method property via dynamic invocation. Internally, the service callback also uses dynamic invocation to call "EndMethodName" to obtain the result. The result is then passed to the original callback using the same ClientEventArgs as seen in the PersonClient example.
Below is the ObjectClient code in its entirety:
#region Copyright //+ Copyright © Jampad Technology, Inc. 2008 //++ Lead Architect: David Betz [MVP] <dfb/davidbetz/net> #endregion using System; using System.ServiceModel; using System.ServiceModel.Channels; //+ namespace Themelia.ServiceModel { public class ObjectClient<TServiceContract> where TServiceContract : class { //- @ClientEventArgs -// public class ClientEventArgs : EventArgs { //- @Object -// public Object Object { get; set; } //- @UserState -// public Object UserState { get; set; } //+ //- @LoadResult -// public TResult LoadResult<TResult>() { if (this.Object is TResult) { return (TResult)this.Object; } //+ return default(TResult); } } //- $ObjectClientState -// private class ObjectClientState { //- @Callback -// public EventHandler<ClientEventArgs> Callback { get; set; } //- @MethodName -// public String MethodName { get; set; } //- @UserState -// public Object UserState { get; set; } } //+ //- $Channel -// private TServiceContract Channel { get; set; } //- $ContractType -// private Type ContractType { get; set; } //+ //- @Ctor private ObjectClient() { ContractType = typeof(TServiceContract); } public ObjectClient(Binding binding, EndpointAddress address) : this() { Channel = new ChannelFactory<TServiceContract>(binding, address).CreateChannel(); } public ObjectClient(String endpointConfigurationName) : this() { Channel = new ChannelFactory<TServiceContract>(endpointConfigurationName).CreateChannel(); } //+ //- @Begin -// public void Begin(String methodName, Object state, params Object[] parameterArray) { Begin(methodName, null, state, parameterArray); } public void Begin(String methodName, EventHandler<ClientEventArgs> callback, Object state, params Object[] parameterArray) { if (parameterArray != null) { Array.Resize<Object>(ref parameterArray, parameterArray.Length + 2); } else { parameterArray = new Object[2]; } parameterArray[parameterArray.Length - 1] = new ObjectClientState { Callback = callback, MethodName = methodName, UserState = state }; parameterArray[parameterArray.Length - 2] = new AsyncCallback(OnCallback); ContractType.InvokeMember("Begin" + methodName, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Public, null, Channel, parameterArray); } //- $OnCallback -// private void OnCallback(IAsyncResult result) { ObjectClientState state = result.AsyncState as ObjectClientState; if (state == null) { return; } Object obj = ContractType.InvokeMember("End" + state.MethodName, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.InvokeMethod | System.Reflection.BindingFlags.Public, null, Channel, new Object[] { result }); if (state.Callback != null) { state.Callback(this, new ClientEventArgs { Object = obj, UserState = state.UserState }); } } } }
Feel free to upgrade it, modify it, or use it however you would like.
Security - Communication over SSL
At this point I would like to turn out attention to a critical part of WCF development: security. Many times, if not most of the time, you will be dealing with some sort of sensitive information in your application. This goes for Silverlight as much as it does for .NET. Fortunately, secure WCF interaction is essentially the same for both. Before we begin though, we need to update our service in order for it to handle secure connectivity. To do this, we are going to create a separate service from our Person service, called SecurePersonService. In most situations, you would just have a single service to handle both, but for the sake of this discussion we're going to keep them separate.
Before we add security to our service though, we need to have an SSL certificate installed on the server. This is the same type of certificate you would have if you wanted to provide a checkout experience on an e-commerce web site. If you already have an SSL certificate, great, if not, then you will need to go through the motions to install one. To install one for IIS6 or IIS7, just follow the instructions at http://blog.functionalfun.net/2008/05/how-to-create-server-certificate-for.html. This post explains how you can easily get a free 90-day signed SSL certificate from a certificate provider.
Once you have the certificate installed, you need to then configure the service for SSL. We haven't yet created the service, but that's OK. We know what it's going to be called. To configure our BasicHttpBinding service for SSL you essentially need to set the security mode to Transport in a binding configuration and specify the certificate in a service behavior. Here's the new configuration:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <system.serviceModel> <bindings> <basicHttpBinding> <binding name="SecureBasicHttpBinding"> <security mode="Transport" /> </binding> </basicHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name="SecurePersonServiceBehavior"> <serviceCredentials> <serviceCertificate findValue="localhost" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" /> </serviceCredentials> </behavior> </serviceBehaviors> </behaviors> <services> <service name="Contact.Service.PersonService"> <endpoint address="" binding="basicHttpBinding" contract="Contact.Service.IPersonService" /> </service> <service name="Contact.Service.SecurePersonService" behaviorConfiguration="SecurePersonServiceBehavior"> <endpoint bindingConfiguration="SecureBasicHttpBinding" address="" binding="basicHttpBinding" contract="Contact.Service.IPersonService" /> </service> </services> </system.serviceModel> </configuration>
There are two conceptual notes that should probably be mentioned at this point:
First, a binding configuration changes the defaults of a particular binding. As I mentioned earlier, a binding is really just a collection of binding elements with some pre-set parameters. In this case, we are applying a particular binding configuration to an endpoint to change the security from "None" to "Transport" (which is SSL in this case).
Second, behaviors essentially allow you to declaratively, either via configuration or programmatically via attributes, modify how a particular part of your service operates. You can have all kinds of different behaviors at different levels. For example, you can use an operation behavior to modify something about an operation. We will see this later when we use an operation behavior to apply operation-level security. In our configuration here, we are applying a service behavior to modify the service to use a particular certificate.
Next, for the time being, let's copy/paste our PersonService implementation as a new class called SecurePersonService. Again, this is only for the sake of this demo. In reality, there's no reason to use the word "Secure" for this service. Then, let's create a SecurePerson.svc file in our service web site root and insert the following service declaration:
<%@ ServiceHost Service="Contact.Service.SecurePersonService" %>
At this point we have an SSL BasicHttpBinding service setup. We can now alter alter our Silverlight client to use SSL. This is really just a matter of setting the BasicHttpBinding security more to Transport and changing the URL from the HTTP based URL to the HTTPS one. Here's how we have modified our previous Silverlight WCF client to allow SSL access:
BasicHttpBinding basicHttpBinding = new BasicHttpBinding(); //+ step 1 of 2: set security mode basicHttpBinding.Security.Mode = BasicHttpSecurityMode.Transport; //+ step 2 of 2: set to https _and_ set proper relative endpoint EndpointAddress endpointAddress = new EndpointAddress("https://localhost/SecurePerson.svc"); IPersonService personService = new ChannelFactory<IPersonService>(basicHttpBinding, endpointAddress).CreateChannel();
Those two steps are the only steps involved. Silverlight is not set to use the new SSL service.
Having said that, if you try to run the Silverlight client, it won't work. Why? Well, as Tim Heuer, Senior Program Manager for Silverlight points out, you can't rely on the existing ClientAccessPolicy.xml configuration we used previously. Previously we allowed the "*" domain, which allows all non-secure clients in. This does not include HTTPS clients. Thus, we need to specify them explicitly. Here is our new ClientAccessPolicy.xml file:
<?xml version="1.0" encoding="utf-8"?> <access-policy> <cross-domain-access> <policy> <allow-from http-request-headers= "SOAPAction"> <domain uri="http://*"/> <domain uri="https://*"/> </allow-from> <grant-to> <resource path="/" include-subpaths="true"/> </grant-to> </policy> </cross-domain-access> </access-policy>
Now our Silverlight client will communicate successfully with the service over SSL. But, SSL only secures the data going over the wire, it doesn't say anything about who can access the data. For this we need to add some form of authentication.
Security - UserName Authentication
If we were working in .NET, we could just use the WsHttpBinding to use the WS-Security standard to send our username and password to our service. Silverlight 2.0, however, doesn't include this binding. This doesn't mean we can't make username/password authentication happen. We could actually use a WebRequest technique that we will cover later to communication with a WsHttpBinding service, but let's keep with BasicHttpBinding for the time being. In this case, all we need to do is add the username and password to the message to be sent over our secure SSL connection and have it checked at the other end. This is really as easy as it sounds.
Regardless of whether you are on the service-side or the client-side, you will have something called an operation context. This houses the information for the current operation. You can think of it as a the WCF operation version of HttpContext in ASP.NET. In fact, much like HttpContext, the OperationContext object is a single. You access it via System.ServiceModel.OperationContext.Current. As you progress in your WCF projects, you will find yourself going to the operation context time and time again for various reason. It's an incredibly important feature of WCF that provides much value to service development on both the service and client side.
Of the many properties in this context, there is one called OutgoingMessageHeaders of type System.ServiceModel.Channels.MessageHeaders. As the name suggestions this houses the message headers that are being sent out. Likewise there's also an IncomingMessageHeaders object of the same type, which contains the header information that has been received. These are just opposite sides of the coin. Both outgoing and incoming headers are like request headers in the web world. Outgoing headers are the "request headers" being sent and incoming headers are the "request headers" received on the other end. This works the same for both the service-side and client-side.
To enable authorization, we just need to add both the username and password to the OutgoingMessageHeader object on the client and read them from the IncomingMessageHeader object on the service. In Silverlight, this looks like this:
MessageHeaders messageHeadersElement = OperationContext.Current.OutgoingMessageHeaders; messageHeadersElement.Add(MessageHeader.CreateHeader("UserName", "", "JohnDoe")); messageHeadersElement.Add(MessageHeader.CreateHeader("Password", "", "MyPassword"));
Notice there is no mention of the service we are going to access. When the service-call happens, the information will automatically be applied in the background and sent over the wire.
Now while this sounds great, we see a reoccurring theme: it doesn't work. Why? If you try to do this, you will find that OperationContext.Current is null. The singleton has not yet been created. Well, that kind of throws a wrench in things. Fortunately, though, this is easily remedied. All we need to do is declare an operation context scope. This basically means instantiating a System.ServiceModel.OperationContextScope object, passing it an existing operation context or something which implements the System.ServiceModel.IContextChannel interface. We obviously don't have the former, but what about the latter?
As it turns out, the channel created by our channel factory doesn't only implement our service contract (IPersonService), but it also implements IContextChannel. Thus, we are able to pass this to the operation context scope, which will then create the operation context, allowing us to add the headers and make our call. Here's our new service call:
using (OperationContextScope scope = new OperationContextScope((IContextChannel)personService)) { MessageHeaders messageHeadersElement = OperationContext.Current.OutgoingMessageHeaders; messageHeadersElement.Add(MessageHeader.CreateHeader("UserName", "", "JohnDoe")); messageHeadersElement.Add(MessageHeader.CreateHeader("Password", "", "MyPassword")); //+ Person person = personService.GetPersonData("F488D20B-FC27-4631-9FB9-83AF616AB5A6"); }
Note that the operation context scope implements the System.IDisposable interface, thus allowing you to wrap it in a using block in C#.
At this point we can turn our attention to the service. This is actually about as simple as what we did on the client-side, if not simpler. All we need to do is see if the IncomingMessageHeaders object on our operation context contains our UserName and Password headers. If they do, then extract them and validate them. If all if well, then run the service logic.
To check to see if a particular message header is in the IncomingMessageHeaders object, use the FindHeader method. This will return the index of the header or -1 if the header was not found. Since both of our headers must exist, the sum of the indexes must be at least 0. -1 would mean that one of the two headers is missing whereas -2 would mean that both are missing. Therefore, if the sum is at least 0, we can grab the header information, which we do using the GetHeader<T> method. This is a generic method, so specify the type that you would like your header value to be as T. In our case they will both be System.String. Once we have pulled the username and password, we can validate them and run our service logic. Here's our entire service up to this point:
using System; using System.ServiceModel; using System.ServiceModel.Channels; //+ namespace Contact.Service { public class SecurePersonService : Contact.Service.IPersonService { //- @GetPersonData -// public Person GetPersonData(String personGuid) { MessageHeaders messageHeadersElement = OperationContext.Current.IncomingMessageHeaders; Int32 id = messageHeadersElement.FindHeader("UserName", "") + messageHeadersElement.FindHeader("Password", ""); if (id > -1) { String username = messageHeadersElement.GetHeader<String>("UserName", ""); String password = messageHeadersElement.GetHeader<String>("Password", ""); if (username == "JohnDoe" && password == "MyPassword") { return new PersonService().GetPersonData(personGuid); } } //+ return null; } } }
Now at this point we could reuse this same technique to secure each operation in our service. However, the technical term for doing this is insanity. This is absolutely crazy. There absolutely has to be a better way of doing this than dealing with the internal mechanics of message headers for each and every single operation in your service. We need something that will validate the username and password information before our logic ever runs so that we never, ever have to see it. Fortunately, there is. This is exactly the value an operation behavior provides. They are also incredibly straightforward to implement.
Before we do that though, we need to create operation invoker. We do this by creating a class which implements the System.ServiceModel.Dispatcher.IOperationInvoker interface. There are many member signatures in this interface, but we only care about the Invoke method. Every time you call an operation, you "invoke" the operation much like how you "invoke" a method via reflection. Earlier we saw how we passed an anonymous method to an Invoke method in WPF so we could access the UI thread. This is the same idea. When you run something, you "invoke" it. For an operation invoker, when an operation is called, the Invoke method is called to run the actual operation logic. Our mission is to house validation logic in our invoker and, if validation succeeds, have the invoker call the original service operation invoker.
We install an operation invoker using an operation behavior. An operation behavior is a class which implements the System.ServiceModel.Description.IOperationBehavior interface. This interface has four method signatures, but we really only care about one of them. Not only that, but the only thing this method will do is set our Invoker. Before this can matter though, we have to have a way to apply our bit to our operation. The operation behavior may set the operation invoker, but who sets the operation behavior? As it turns out, operation behaviors may be applied declaratively through attributes. This is rather interesting as it allows us to have our operation behavior class inherit from System.Attribute, thus making our operation behavior completely self-sufficient.
It's that time again to move from the lecture hall to the lab. We will first turn our attention to the operation behavior. Below is the entire operation behavior:
using System; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; //+ namespace Contact.Service.Behavior { [AttributeUsage(AttributeTargets.Method)] public class SecurityOperationBehavior : Attribute, IOperationBehavior { //- @AddBindingParameters -// public void AddBindingParameters(OperationDescription operationDescription, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { //+ blank } //- @ApplyClientBehavior -// public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation) { //+ blank } //- @ApplyDispatchBehavior -// public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) { dispatchOperation.Invoker = new SecurityOperationInvoker(dispatchOperation.Invoker); } //- @Validate -// public void Validate(OperationDescription operationDescription) { //+ blank } } }
As you can see, it's just a simple attribute whose attribute usage states that it may only be applied to methods. Notice also that the only method we care is the ApplyDispatchBehavior method and, as mentioned before, the only thing were doing is setting the new invoker, passing the original invoker to its constructor. Let's now turn to our operation invoker:
using System; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; //+ namespace Contact.Service.Behavior { public class SecurityOperationInvoker : IOperationInvoker { //- $InnerOperationInvoker -// private IOperationInvoker InnerOperationInvoker { get; set; } //+ //- @Ctor -// public SecurityOperationInvoker(IOperationInvoker operationInvoker) { this.InnerOperationInvoker = operationInvoker; } //+ //- @AllocateInputs -// public Object[] AllocateInputs() { return InnerOperationInvoker.AllocateInputs(); } //- @Invoke -// public Object Invoke(Object instance, Object[] inputs, out Object[] outputs) { //+ authorization MessageHeaders messageHeadersElement = OperationContext.Current.IncomingMessageHeaders; Int32 id = messageHeadersElement.FindHeader("UserName", "") + messageHeadersElement.FindHeader("Password", ""); if (id > -1) { String username = messageHeadersElement.GetHeader<String>("UserName", ""); String password = messageHeadersElement.GetHeader<String>("Password", ""); SecurityValidator.Authenticate(username, password); //+ return InnerOperationInvoker.Invoke(instance, inputs, out outputs); } //+ throw new FaultException<InvalidOperationException>(new InvalidOperationException(SecurityValidator.Message.InvalidCredentials), SecurityValidator.Message.InvalidCredentials); } //- @InvokeBegin -// public IAsyncResult InvokeBegin(Object instance, Object[] inputs, AsyncCallback callback, Object state) { return InnerOperationInvoker.InvokeBegin(instance, inputs, callback, state); } //- @InvokeEnd -// public Object InvokeEnd(Object instance, out Object[] outputs, IAsyncResult result) { return InnerOperationInvoker.InvokeEnd(instance, out outputs, result); } //- @IsSynchronous -// public Boolean IsSynchronous { get { return InnerOperationInvoker.IsSynchronous; } } } }
At first glance it may seem like there's a lot going on here, but if you loose closely you will see that there are only three things of note here. First, there is the InternalOperationInvoker property. This just hold the original operation invoker so we can call it later. Second, every other member except Invoke does nothing but call the invoker in InternalOperationInvoker. Third, the Invoke method is not really doing anything but what we've already seen in the service operation. The only difference is that upon validation, instead of doing the actual logic, the Invoke method just calls the Invoke method of our InternalOperationInvoker property. So, it's not really all that busy after all.
With our operation behavior created, we can now return to modify our service operation. All we need to do is remove all the validation logic and apply our new SecurityOperationBehavior attribute. Our service operation now looks like this:
//- @GetPersonData -// [SecurityOperationBehavior] public Person GetPersonData(String personGuid) { return new PersonService().GetPersonData(personGuid); }
That's a lot better than before, isn't it? With this new toy at your disposal, whenever you want to secure a new operation in your service, just apply the attribute and move on.
Before moving on, note that if you would like to see another example of using operation behaviors, see my Minima Blog Engine 3.1 source code. Minima is my training software to demonstrate many areas of .NET and it relies heavily on WCF.
Handling Exceptions
We now have an authentication model for our WCF connectivity in our Silverlight application. There is one more thing we could do to improve this. Returning null from a service when validation failed is a good way to cause no end to confusion. The worst error is seeing no error. Therefore, we need to throw an exception if validation fails. Continuing in how we have been discussing things, let's move our attention back to the .NET world to discuss some concepts and theory. Then let's angle our knowledge a bit to make way for the Silverlight world.
To begin, you need to understand that the concept of an exception is a .NET concept. The messages over the wire have absolutely nothing to do with .NET and, therefore, have no idea what an exception is. Instead, messages over the wire and service-oriented architecture in general have a concept called a fault, which essentially serves the same purpose as an exception. Therefore, if you were to throw an exception in a service, the SOA world won't have the first clue what you're talking about. Not only that, but when your client hears about the exception, it won't see an exception, but, rather a signal that there was some sort of a catastrophic error, thus closing your client channel (our IPersonService instance). In other words, the following in the service will break your client:
String message = "Invalid credentials"; throw new System.InvalidOperationException(message);
You can, however, get around this by telling your WCF service to include exception information in fault. You can set this in configuration or in your service host factory. I'm not going to demonstrate this because I don't want to encourage this cop-out technique. This is only to be used for debugging. Do you really want the fully-detailed exception message and stack trace to go out into all the world to see? This would be even worst than turning ASP.NET custom errors off. This is the image of a security flaw.
WCF provides the proper mechanism for dealing with exceptions with the System.ServiceModel.FaultException<T> class. This allows us to to throw exceptions as fault. There's also a System.ServiceModel.FaultException, from which FaultException<T> inherits. You can either throw the non-generic one to send a simple message or you can use the generic one to send a message and the exception type. The generic one will probably be the one you use most often.
To use FaultException<T>, first instantiate the exception you would like to throw then pass that object as well as the exception message to the FaultException<T> instance. Then you may throw the FaultException<T> instance. That is, instead of throwing the InvalidOperationException as seen in the last example, do the following:
String message = "Invalid credentials"; throw new FaultException<System.InvalidOperationException>(new System.InvalidOperationException(message), message);
This will allow your client code to catch the exception gracefully and won't send your private stack trace for the world to see. Here's the code to handle this:
try { person = personService.GetPersonData("F488D20B-FC27-4631-9FB9-83AF616AB5A6"); } catch (FaultException ex) { Response.Write(ex.Message); }
At this point our service-side fault exception code is the same as the following:
throw new FaultException("Invalid credentials");
As I've already mentioned, using the FaultException class will send back a message. This is exactly what FaultException<T> at this point too. To make FaultException<T> work the way it was designed, you need to declare a fault contract on your service-side service contract. You do this by applying the System.ServiceModel.FaultContract attribute to your operation contract. Here's our new service contract:
using System; using System.ServiceModel; //+ namespace Contact.Service { [ServiceContract(Namespace = Information.Namespace.Contact)] public interface IPersonService { //- GetPersonData -// [OperationContract] [FaultContract(typeof(InvalidOperationException))] Person GetPersonData(String personGuid); } }
Now we are able to catch the various different fault exceptions a service may return:
try { person = personService.GetPersonData("F488D20B-FC27-4631-9FB9-83AF616AB5A6"); } catch (FaultException<InvalidOperationException> ex) { Response.Write(ex.Message); }
At this point some may suggest that, since we are only interested in security validation, we should turn our attention toSystem.Security.SecurityException as follows:
throw new System.Security.SecurityException("Invalid credentials");
This special exception will actually be a fault over the wire, give you graceful error handling, and it also doesn't publish your stack trace. One thing you need to remember about this exception, though, is that you don't catch the same exception that has been thrown. You actually catch System.ServiceModel.Security.SecurityAccessDeniedException. Not only that but the message that was sent from the service, isn't the message that will show in your client. The client side exception will always say "Access is denied.". Here's an example:
try { person = personService.GetPersonData("F488D20B-FC27-4631-9FB9-83AF616AB5A6"); } catch (System.ServiceModel.Security.SecurityAccessDeniedException ex) { Response.Write(ex.Message); }
You probably shouldn't rely on this mechanism though. WCF will replace the SecurityException with a Fault over the wire, but not in all places. For example, the above code will work beautifully if you are throw the exception from an operation, but if you throw it in an operation behavior, your client will see, not SecurityAccessDeniedException, but rather a FaultException (not the generic one either). You should use FaultException<SecurityException> either as this can cause the real exception to be System.Net.Sockets.SocketException, which is absolutely catastrophic. For a security validation error, I use FaultException<InvalidOperationException> as we've already seen.
Now, I must apologize at this point for getting you excited about all the awesomeness of faults handling in WCF, because... none of that works in Silverlight. Yeah. I know. It's like "why didn't you say that earlier!!??" Well, it's important to understand the underlying mechanics of the ideal, before you can work around them into the real-- otherwise, you're just making stuff up as you go. Not only that, you need to make sure you understand WCF in general before you understand WCF in Silverlight. Chances are that your WCF services will do more than simply serve Silverlight.
The reason none of this works in the Silverlight world is because has absolutely no concept of a fault. It actually can't. With this specific service, when your service call fails validation, your Silverlight application will only see a 404 error with the text "The remote server returned an error: NotFound". When a fault is thrown, regardless of how pretty you make it, the HTTP status code is not 200, where 200 means that everything went OK. This is a problem for Silverlight.
Silverlight, as we know, runs in the context of a web browser. Because of this, Silverlight is only provided the information that a web browser allows it. When a response is returned to Silverlight with a status code other than 200, the web browser intercepts it and never tells Silverlight about it. It's kind of like when you're at the office and you go to search for a critical piece of information only to find that your company's content filter blocks that particular technology web site thinking it was inappropriate.
There are various ways around this and every single technique that I know of requires you to modify your service. The method we are going to use modifies the service a tiny bit, but it does so in a reusable way. We are essentially going to create a custom FaultDetail class which we will implement in our Person data contract. If an exception is thrown on the service, a fault detail will be created and added to our Person object. Then, when we receive a response on the client side, we can check to see if FaultDetail is null. If not, then there was a fault.
However, I don't want to



