Philippe Truche’s Blog

20 August 2011

Throwing SOAP Faults in a WCF service being mindful of non-WCF clients

Filed under: .NET, WCF, Web Services — Tags: , , , , , , — Philippe Truche @ 9:28

I was engaged recently to design and write a WCF service that is to be consumed by an ASP.NET 2.0 client.  While I was aware that with a WCF service and a WCF client, using a System.ServiceModel.FaultException<TDetail>  was ideal for handling exception conditions, I wasn’t sure how this was going to be handled by .NET 2.0 since FaultException<TDetail> was introduced in .NET 3.0 – in .NET. 2.0, a SOAP faults are caught with  a System.Web.Services.Protocols.SoapException.

So off to trial and error I went and here is what I proposed.

Firstly, it is important to write a WCF service that can be consumed by a WCF client as well as a non-WCF client.  To this end, throwing SOAP faults should be done in a manner that both WCF clients and non-WCF clients can understand.  My first step was to make sure I had a standard WCF service with a WCF client that could understand the SOAP faults.  I created a DataContract for the SOAP fault I wanted to throw when certain conditions were met:

///<summary>
/// Thrown when no matching MPI is found.
/// </summary>
[DataContract(Namespace = WcfConfiguration.XmlNamespace)]
public class MpiNotFoundFault
{
	private string _mpi;

	///<summary>
	/// Gets or sets the MPI.
	///</summary>
	[DataMember]
	public string Mpi
	{
		get { return _mpi; }
		set { _mpi = value; }
	}
}

I  then specified my FaultContract on the service interface definition as follows:

[ServiceContract]
public interface IMyPinService 
{
	[OperationContract] 
	[FaultContract(typeof(MpiNotFoundFault), Name ="MpiNotFoundFault", Namespace = WcfConfiguration.XmlNamespace, Action = WcfConfiguration.XmlNamespace +"/MpiNotFoundFault")] 
	bool RequestPin(string mpi, DateTime dob);
}

So far, so good.  Now, let’s see how we might throw this SOAP fault in the service implementation.  For the WCF client to work, I threw the exception as follows:

public bool RequestPin(string mpi, DateTime dob) 
{
	// Code elided
	throw new FaultException<MpiNotFoundFault>(new MpiNotFoundFault() { Mpi = mpi });
}

Then, in the WCF client, all I have to do is this:

// With a WCF client, we can catch specific exceptions and get to each fault contract&quot;s 
// contents by accessing the ex.Details propery.
catch (FaultException<MpiNotFoundFault> ex)
{
	Console.WriteLine(“MPI {0} was not found.“, ex.Detail.Mpi);
}

Yes, WCF does a lot of work for us.  However, when your client is not WCF, SOAP faults get a little bit more complicated.  Fortunately, there is a SOAP specification that can be referred to.  The SOAP 1.2 specification on soap faults is located here: http://www.w3.org/TR/soap12-part1/#soapfault.  Notice I am introducing you to SOAP 1.2, not SOAP 1.1.  Yet, in the .NET 2.0 framework, when you use wsdl.exe to create a proxy class, the SOAP 1.1 protocol is defaulted.  That’s somewhat of a problem because is SOAP 1.1, faults may only have a faultcode and a faultstring.  It wasn’t until SOAP 1.2 that hierarchical codes and subcodes were defined.  To see the difference between the two, see http://hadleynet.org/marc/whatsnew.html#S3.1.4.

Why is this important you ask?  Well, if in my ASP.NET client (that does not know anything about WCF) all I have is a SoapException, then I’d like to use the code and subcode of the faults to communicate detail about the exception (of course, my service consumer is a trusted source and will filter the information appropriately before passing it on to its users, but that’s another topic entirely).

Here is what I want to do. In my ASP.NET client, I want to check the code and subcode to understand what the nature of the problem was.  Here is what my ASP.NET catch statement looks like this:

// In WCF we can catch specific exceptions by using FaultException<T> where T is one of the fault contracts 
// included in the service operation. In contrast, ASP.NET web services only allow for catching SoapException. 
// To get to the detail, use the SOAP Fault Code and Subcode as shown below. 
catch (SoapException ex)
{
	// The SOAP fault code always contains the string ‘CredentialValidationRequestFailed’
	Debug.WriteLine(ex.Code.Name); // prints the string "CredentialValidationRequestFailed"
	// The SOAP fault subcode contains the actual soap fault name without the Fault suffix
	Debug.WriteLine(ex.SubCode.Code.Name); // prints the string "MpiNotFound"
}

By doing this, I can see that my call failed because of a CredentialValidationRequestFailed code.  This code could occur for any number of reasons.  In this example, it occurred because of an MpiNotFound subcode, but I have another 3 subcodes that could have been the cause of the fault.

So what do I need to do to achieve this? Well I need to use SOAP 1.2 for sure.  On the service side, I configure the service using an HttpBinding. By default, this binding uses SOAP 1.1. and it can’t be changed. To use SOAP 1.2 for message encoding, I create a custom binding. My configuration file now looks like this:

  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="basicHttpSoap12Binding">
          <textMessageEncoding messageVersion="Soap12"/>
          <httpTransport/>
        </binding>
      </customBinding>
    </bindings>
    <services>
      <service name="MySoap12Service">
        <endpoint address="" binding="customBinding" bindingConfiguration="basicHttpSoap12Binding"
          bindingNamespace="MySoap12ServiceNamespace"
          contract="MySoap12Service">
        </endpoint>
      </service>
    </services>
  </system.serviceModel>

On the client side, I use wsdl.exe to generate the proxy class; instead of letting the default SOAP 1.1 protocol apply, I pass in the protocol switch to specify the SOAP 1.2 protocol should be used as follows:  wsdl.exe /protocol:SOAP12. This causes the generated proxy to specify the SOAP 1.2 protocol in its constructor:

public partial class MyService : System.Web.Services.Protocols.SoapHttpClientProtocol {
    // code elided
        
    public MyService() {
        this.SoapVersion = System.Web.Services.Protocols.SoapProtocolVersion.Soap12;
        // code elided
    }
    
    // code elided

Then, I need to throw my SOAP faults with a bit more information.  Here is the revised code snippet for throwing a SOAP fault that can now be understood by non-WCF clients:

throw new FaultException<MpiNotFoundFault>(new MpiNotFoundFault() { Mpi = mpi },
	String.Format(CultureInfo.InvariantCulture, “MPI ‘{0}’ not found.“, mpi),
	new FaultCode(“CredentialValidationRequestFailed“, new FaultCode(“MpiNotFound“, WcfConfiguration.XmlNamespace)));

That’s it.  I throw the FaultException<MpiNotFoundFault> exception, passing in a new MpiNotFoundFault object into the constructor, then I pass in the reason as the “MPI ‘xyz’ not found” string, and then I create my code and subcodes.  And voila, I have happy WCF clients and happy non-WCF clients.

On the wire, the SOAP fault looks like this:

<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope">
  <s:Header />
  <s:Body>
    <s:Fault>
      <s:Code>
        <s:Value>s:CredentialValidationRequestFailed</s:Value>
        <s:Subcode>
          <s:Value xmlns:a="MyNamespace">a:MpiNotFound</s:Value>
        </s:Subcode>
      </s:Code>
      <s:Reason>
        <s:Text xml:lang="en-US">MPI '123123' not found.</s:Text>
      </s:Reason>
      <s:Detail>
        <MpiNotFoundFault xmlns="MyNamespace" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
          <Mpi>123123</Mpi>
        </MpiNotFoundFault>
      </s:Detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

2 June 2008

WCF, Conditional Compilation, and language differences

Filed under: .NET, WCF, Web Services — Tags: — Philippe Truche @ 11:46

Perhaps you might have defined WCF message contracts lately, and you want to be explicit about the encryption level you require for your message parts.  I am taking the example of a message contract, but really my point is applicable to any element to which you wish to apply the EncryptionLevel enumeration.

In C#:

[MessageHeader(Name="Environment", MustUnderstand=true
#if !DEBUG
 , ProtectionLevel = ProtectionLevel.Sign
#endif
)]

public String Environment
{
 get { return _environment; }
 set { _environment = value; }
}
In VB.NET:

#If CONFIG = "Debug"
 <MessageHeader(Name:="Environment", MustUnderstand:=True)> _
 Public Property Environment() As String
#Else
 <MessageHeader(Name:="Environment", MustUnderstand:=True, ProtectionLevel:=ProtectionLevel.Sign)> _
 Public Property Environment() As String
#End If
 Get
 Return _environment
 End Get
 Set(ByVal Value As String)
 _environment = Value
 End Set
 End Property

 

 
As you can see, there are some key differences in how attributes are specified using conditional compilation:
  • In C#, I can toggle parts of the same attribute on and off.  In VB.NET, I must repeat the entire attibute (no parts allowed by the compiler).
  • Because of line continuation constraints in VB.NET, the line following the line being continued must be included inside the conditional compilation block.
  • Using VB.NET in Visual Studio (2005 or 2008), it is not clear which part of the conditional compilation is “active” based on the selected configuration.  In C#, the inactive block is grayed out.

Keep in mind these “consequences” as you proceed with WCF attributes in the language you write with.

25 April 2007

Using Visual Studio Team System to load test web services

Filed under: .NET, Testing, VSTS, Web Services — Philippe Truche @ 1:55

I recently had to load test web services for the smart client application we have developed.  I wanted to be able to go through quick cycles (test, evaluate, adjust) without having to wait for a few days for a load test slot in our LoadRunner lab, so I offered to use Visual Studio team system to perform the tests.  I already had experience with Microsoft’s previous load test tool – Application Center Test.  I was pleased with VSTS’ version of a load test tool.  Not quite as powerful as LoadRunner, but much more accessible in ease of use.

Anyway, I used VSTS successfully to load test web services and satisfy the following requirements:

  • Randomly pick data from a set for a given test.
  • Invoke the web services using HTTP POSTs (i.e. without having to create a client)
  • Use a configuration file to store the SOAP envelopes needed to make the web service calls.

Picking data randomly 

When using VSTS tests, the test methods are decorated with the [TestMethod] attribute.    The [DataSource] attribute can also be used on the test method to indicate that the test is data-driven.  Caution: the test is executed as many times as there are rows in the data source.  That may be OK when performing unit tests, but it is not OK for a load test.  So how do you get around this?  Well, I got my data source to return only one row picked randomly.  Using SQL Server 2005 Express, I defined views that would select one row of data using the following construct: “SELECT TOP(1) columnName1 AS alias1, columnName2 AS alias2, …. , columnNameN as aliasN FROM tableName ORDER BY NEWID();”

You might wonder why I am using an aliases on the column names.  The reason why I did this was to decouple the column names in the tables from the element names in the SOAP envelopes.  When the data source provides the row of data, I have a utility running through the column names of the DataRow object and substituting the data in the SOAP envelope based on the name of the columns found in the DataRow provided through the [DataSource] attribute.  This simple convention makes it very easy to parameterize the SOAP sent to the service.  Because the load test client should be as efficient as possible, I use a simple string parsing algorithm to perform the substitutions.

Invoke the web services using HTTP POSTs

I created a static class that uses an HttpWebRequest object to create the POST.   The result is returned into an HttpWebResponse object by simply doing this:

using ( HttpWebResponse response = (HttpWebResponse)request.GetResponse())

{

return response.StatusCode.ToString();

}

If the web server responds with error response codes (e.g. HTTP 401, HTTP 500), VSTS detects that and reports on the errors.  This is much better than Application Center Test where I was left to parse through the responses to make sure I did not get an error back.

Use a configuration file to store the SOAP envelopes needed to make the web service calls.

For this, my team member created an XML file that is loaded into an XmlDocument from which XmlNodes are selected and stored into an object that holds the SOAPAction to perform, the SOAP envelope to send the service, and the Url to use.  Yes, I know, this is very ASMX-centric, and will likely need to change with WCF, but we were not developing a framework either.  We needed a point solution to get going quickly.

Leave a comment if you have any questions or comments.

Blog at WordPress.com.