So plenty has been written about the mismatch in between the Silverlight browser plug-in and WCF messaging with fault use cases over HTTP. For those of you that didn't know about this then here's your chance to catchup:
According to the documentation,
SOAP faults are not supported in the WCF client implementation for Silverlight
(even for Silverlight 4). It turns out that there’s a little more to the
story and the support isn’t black and white. The main reason that SOAP
faults are not supported is because all requests go through the browser
networking stack and the only two HTTP status codes supported are 200 and 404
(or as my associate Chris Martinez stated, "yeah – 'nice'”).
So if this is the reason why you are here, you'll be glad to know there's two ways to handle this (and they can actually be combined in fact).
introduced a new concept called the “client networking stack”. If (and
only if) a service request is configured to use the client networking stack, it
is possible to get and handle a SOAP fault provided the HTTP status isn't one of the afore mentioned codes. For more details you can read the excellent
post from Jeff Prosise that explains the features and details of
configuring the client networking stack. Jeff’s post points out that authentication isn’t possible in the client stack, or to be better put, must be manually accomplished by you the developer and that any existing authentication session between the browser and the server is not shared. Mike
Taulty has a great post with some examples that demonstrate how to leverage
the client networking stack in Silverlight 4 with authentication.
Unfortunately, for it to work, you’d have to use Basic Authentication which
would require a method of capturing and securely storing network
credentials in memory or re-requesting them from the client. This may or may not be an issue in your application space/design. For instance, if you run a Silverlight application that performs authentication INSIDE the running application itself (such as a case where you actually capture the UID/PWD as part of the normal flow of the application or if the services are anonymous already) then this actually isn't an issue. You can go on your merry way. In addition, believe it or not, you can actually work with WS-Trust secured soap services provided that:
OK, OK you should note that item #3 above really just moved the previously mentioned manual authentication story from the service to the STS so all the same rules apply. For details so take a look at my posted example on how to perform this type of work.
So if you are finding yourself in this issue and you want to do the lifting manually, you can do switch over to the client networking stack with this simple bit of code:
Boolean didRegister = WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);
Yup, that's it! now be aware that because this works on address prefix matching, this means that you can select by protocol or by path to varying degrees of fidelity that you can select different remote systems to use different approaches. This isn't a pancea but it sure does provide some needed wiggle room for you.
But remember, this isn't the only game in town...
So if we step back and think about this for a second, what exactly is a fault? It's a message of a known schema that is used to represent an issue or non-standard result for a operation. Could this mean an error? Sure why not? That sounds like a perfectly fine use of the structure. Does it HAVE to be an error? Of course not! If you come out of this understanding one thing and one thing only it should be this: A Fault is NOT and exception, though most exceptions are handled via faults. Here's an example fault message in soap:
<!-- Headers elided for clarity. -->
What this should be telling you is that what you are stating when you add a fault contract to an operation that you are declaring that such and such a soap action can have this set of possible response actions, one of which represents the "normal" operation (the reply action) and any others are fault actions. Think of this as actually being overloading by on the return types in a polymorphic way. This isn't something most languages do well so this makes sense that WCF uses exceptions here. However you should notice that regardless of intention, that WCF will default to using 500 on any outgoing fault, period.
I'm going to go out on a limb here and state that this doesn't make sense from a contract sense. After all, from an HTTP perspective, in a fault situation you can get an HTTP200 status code. The HTTP part of the story DID work after all; The message was posted and a reply came back. That’s not something we should “exception” on as the FaultException<T> class is really just a way to abort and return a fault message to a WCF client by hijacking SEH in .Net runtime. When it comes to HTTP status code, this part of WS-BPI isn’t actually defined behaviorally as you can return whatever HTTP code you want.
Personally I think this is a bad design choice because IF you threw a known fault type action from the contract as this would then be ”Expected” behavior for your service and isn’t an error per say as opposed to the wcf:InternalRecieverFault fault code where a 500 makes sense. My service didn't blow up, it returned one of an expected set of messages to the caller. Least of all let us not forgot that WCF is wider than just HTTP which in all honesty just plan SUCK BALLS as a messaging protocol but it is what we are stuck with for supporting Silverlight clients (yes I know that there's the 4.0 proprietary tcp transport binding now but that's beside the point).
We all must remember that what makes a fault a fault is this: The content of the message just happens to contain an XML document that is a soap:Fault element under the soap:envelope/soap:Body element. This is where raw Message class instances really shine as they don’t care about what’s in the message body. Because faults are such a core concept they have a super convienent IsFault property that basically just tells you if the first body element is soap:Fault but that’s just sugar. Or as I keep telling people, “It’s all about the message”.
So if this is up your alley then great. The real issue of course is how to change the status. Here we can use a custom MessageInspector on your service that takes a look at the fault message and takes appropriate action based on the soap action. If it is “expected” then set the HTTP status code to 200. A known fault isn’t an exception, it’s an expected message content for return and consumption on the client.
So let's take a look at the code to handle this. In order to handle this you want to implement IDispatchMessageInspector, and more specifically, the BeforeSendReply method. In this method, you receive the ready to be returned Message class instance representing the reply. Be aware that this occurs for ANY message, not just faults. All you need to do is run your logic to determine if the message is a fault and is well-known as defined in the WSDL. If so, then set the HttpResponseMessageProperty.StatusCode value to the appropriate HttpStatusCode value (in this case, 200).
void IDispatchMessageInspector.BeforeSendReply(ref Message reply, Object correlationState)
if (!reply.IsFault) return;
if (!this.IsKnownFault(reply)) return; // IsKnownFault wraps up your logic. Omitted for brevity
var httpProperties = reply.Properties[HttpResponseMessageProperty.Name] as HttpResponseMessageProperty;
Debug.Assert(httpProperties != null);
if (httpProperties == null) return;
httpProperties.StatusCode = HttpStatusCode.OK;
Great, but how do we get this inspector in the channel pipeline? Well we do that with a WCF behavior! There's plenty of example document on how to use behaviors so I will not bother with duplicating a redundant example. So for a bit of ready to use code, take look at the attached files to see how to do this work (pay special attention to how I determine if a service has faults and what those contracts are).