Occasionally you may need to execute some SOAP operations using a client that doesn't understand SOAP. If this client doesn't like the HTTP 500 that comes back when the server generates a SOAP Fault (behavior specified in the SOAP spec), you may not be able to read the content that comes back, with the actual fault XML in it.
For example, I recently needed to use URLLoader and HTTPService to access a SOAP service in Flex, because the WebService (SOAP) implementation didn't like the server's WSDL. Admittedly, the WSDL included some legacy XML schema, and was a bit unusual, but it did validate. So it seems plausible that other valid services might also be inaccessible to the Flash/Flex WebService or other SOAP clients.
With HTTPService and ActionScript 3 E4X support, it's not terribly painful to do the SOAP client work yourself... until you get a SOAP Fault. The 500 causes HTTPService and URLLoader to abort and they do not return the document content to the application.
In this instance, I couldn't alter the web service itself, and didn't have the resources to build a new HTTP client on flash.net.Socket. That's the setup. Here's the hack, for IIS/ASP.net services:
Concept: alter the ASP.Net processing pipeline to return OK (HTTP 200) in place of a 500, but only when the SOAP fault is the cause of the 500. The way to do this is to use an ASP.Net facility called a Soap Extension to allow programmatic reading and/or writing to the SOAP message stream as it passes into and out of the application. Find the message in the state you don't like. Alter it. Done.
- Create a C# DLL project in Visual Studio and pull in boilerplate code for a System.Web.Services.Protocols.SoapExtension (MSDN docs and MSDN magazine have sample code, or look here)
- Pull out everything you don't need (which is most of it, if you're looking at a real-life sample)
- Locate the ProcessMessage method, and check for the SoapMessageStage.AfterSerialize lifecycle value. In most samples, there's a switch statement on the SoapMessageStage, so you can just identify the proper branch (the others should be blank for this basic solution)
- Add the following code:
if (message.Exception != null)
HttpContext.Current.Response.StatusCode = 200;
- Build. Place resultant DLL with other precompiled binaries used by the ASP.Net application.
- To tell ASP.Net to use this extension, add the following XML tag to the application's web.config, and all SOAP calls will use your extension. Find (and/or create) the <soapExtensionTypes> tag (under <webservices>, under <system.web>), and add
<add type="YourSoapExtNamespance.YourSoapExtClassName, YourSoapExtAssemblyName" priority="1" group="High" />
That's it. One line of code, a little configuration, and your hack is on. For a little more context, I've put the full SoapExtension class here.