views:

23

answers:

1

ASP.Net 2.0 Web Services automatically create both SOAP 1.1 and SOAP 1.2 bindings. Our web service, however, has SOAP extensions and custom exception handling that make the assumption that only the SOAP 1.1 binding is used (for example, the SOAP extension uses the HTTP SOAPAction header to control behavior).

I am looking to correct the code that makes these assumptions and make it work with either SOAP 1.1 or SOAP 1.2 properly. I am running into a bit of a problem in the generation of elements for our SOAP faults.

Consider the following web method implementation:

[WebMethod]
public void
ThrowsSoapException()
{
    throw new SoapException("This is a SOAP exception.", SoapException.ServerFaultCode);
}

Invoking this via SOAP 1.1 yields the following result:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
   <soap:Body>
      <soap:Fault>
         <faultcode>soap:Server</faultcode>
         <faultstring>This is a SOAP exception.</faultstring>
         <detail/>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

Invoking via SOAP 1.2 yields the following result:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
   <soap:Body>
      <soap:Fault>
         <soap:Code>
            <soap:Value>soap:Receiver</soap:Value>
         </soap:Code>
         <soap:Reason>
            <soap:Text xml:lang="en">This is a SOAP exception.</soap:Text>
         </soap:Reason>
         <soap:Detail/>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

In both these cases there is an empty detail element as a child of the <soap:Fault> element, but it has a different qualified name, either <detail> or <soap:Detail>.

Now consider the following code that tries to create a SOAPException with a detail element.

[WebMethod]
public void
ThrowsSoapExceptionWithDetail()
{
    XmlDocument doc = new XmlDocument();
    XmlNode detail =
        doc.CreateNode(XmlNodeType.Element, SoapException.DetailElementName.Name, SoapException.DetailElementName.Namespace);
    XmlNode custom =
        doc.CreateNode(XmlNodeType.Element, "custom", "http://example.com/xml/namespace/blah");
    custom.InnerXml = "Detail value";
    detail.AppendChild(custom);
    throw new SoapException("This is a SOAP exception with a detail element.", SoapException.ServerFaultCode, Context.Request.Url.AbsoluteUri, detail);
}

The SOAP 1.1 response is:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
   <soap:Body>
      <soap:Fault>
         <faultcode>soap:Server</faultcode>
         <faultstring>This is a SOAP exception with a detail element.</faultstring>
         <faultactor>http://localhost/simplewebservice/service1.asmx&lt;/faultactor&gt;
         <detail>
            <custom xmlns="http://example.com/xml/namespace/blah"&gt;Detail value</custom>
         </detail>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

and the SOAP 1.2 response is:

<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
   <soap:Body>
      <soap:Fault>
         <soap:Code>
            <soap:Value>soap:Receiver</soap:Value>
         </soap:Code>
         <soap:Reason>
            <soap:Text xml:lang="en">This is a SOAP exception with a detail element.</soap:Text>
         </soap:Reason>
         <soap:Node>http://localhost/simplewebservice/service1.asmx&lt;/soap:Node&gt;
         <detail>
            <custom xmlns="http://example.com/xml/namespace/blah"&gt;Detail value</custom>
         </detail>
      </soap:Fault>
   </soap:Body>
</soap:Envelope>

The SOAP 1.2 response now has the wrong qualified name for the detail element. It should be <soap:Detail>, but instead is merely <detail>, same as the SOAP 1.1 response.

It seems that the ASP.Net 2.0 framework has done quite a bit to transform a SOAPException into the appropriate form for the SOAP version, but neglected to properly handle the detail element. Additionally, they don't seem to have exposed the correct SOAP 1.2 qualified name for the detail element as was done with the SoapException.DetailElementName property.

So, what is the correct way to add a detail element to a SOAP fault response that works for both SOAP 1.1 and SOAP 1.2? Do I need to detect the SOAP version myself and hard-code the SOAP 1.2 qualified name for the detail element?

A: 

The following is not meant to be snide:

The fix to this problem is to use WCF.

This looks like a bug in ASMX web services handling of the detail element in a SOAP 1.2 fault. It obviously makes no sense for the qualified name of the element to change based on whether or not the element has a value.

You could report this error on Connect, but since only critical ASMX bugs are being fixed, that's unlikely to help you.

I doubt that WCF has this problem, since it fully supports SOAP Faults.

John Saunders
Unfortunately, moving off of .Net 2.0 just isn't possible right now.I'm also pretty certain that WCF will come with its own new set of problems, similar to the ones we have already solved with custom extensions, etc., in the .asmx world.I would certainly like to have the luxury of exploring this in the long term, though.
GBegen
@GBegen: Microsoft would have been extremely foolish to not learn from the mistakes of ASMX when creating WCF. In particular, the extensibility model is _far_ cleaner and much more comprehensive. Amont other things, you can take over error processing for the entire service, and you could override the serialization of faults if needed. Except that it would probably not be needed, since WCF supports faults natively - a WCF client receiving a fault response with a fault named `SomeFault` receives a `FaultException<SomeFault>` exception.
John Saunders