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>
Advertisements

14 July 2008

Using the Enterprise Library Validation Block with WCF Asynchronous Operations

Filed under: .NET, WCF — Tags: , , , , — Philippe Truche @ 3:58

I was recently surprised to find out first hand that the Enterprise Library Validation Block throws a somewhat obscure exception: ArgumentNullException(“operation.SyncMethod”) when the WCF AsyncPattern is employed.

By debugging into the Enterprise Library source code (EntLib 3.1), I came to understand that the Validation Block was coded to handle synchronous service operations only (using the FaultContract<ValidationFault> attribute on the service operation signature .  In our case, some of our operations also implement the asynchronous pattern and we have Begin and End method pairs for each.

While I was aware that one-way operations do not allow faults to be returned, I was’t sure about asynchronous service operations (implemented as asynchronous on the service side).  I checked the .NET documentation and was able to confirm that FaultContract<T> is supported on service operations implemented asynchronously using the AsyncPattern property.   A difficult to find comment exists in the documentation of the FaultException(TDetail) Generic Class: “The FaultContractAttribute can be used to specify SOAP faults for both two-way service methods and for asynchronous method pairs.”  See http://msdn.microsoft.com/en-us/library/ms576199.aspx.

With this information in hand, I reasonned that I should be able to modify the validation block.  I made the code changes, ran the NUnit tests included in the validation block, and employed the modified assembly on our asynchronous service operations.  I made modifications to ValidationParameterInspector.cs and ValidationBehavior.cs.  The scope of the modifications included the following methods:

  • ValidationParameterInspector.ValidationParameterInspector
  • ValidationParameterInspector.BeforeCall
  • ValidationBehavior.HasValidationAssertions

Note that the modifications apply to both EntLib 3.1 and EntLib 4.0.  The modifications are included below:

Changes to ValidationParameterInspector.cs

/// <summary>

/// Constructor to initialize <see cref=”InputValidators”/>.

/// </summary>

/// <remarks>

/// When the operation is a <see cref=”OperationDescription.BeginMethod”/> because it implements the

/// the <see cref=”OperationContractAttribute.AsyncPattern”/>,

/// it will cause the <see cref=”InputValidators”/> to have empty validators for the

/// <see cref=”AsyncCallback”>callback</see> and <see cref=”Object”>state</see> method

/// arguments.

/// </remarks>

/// <param name=”operation”>The <see cref=”OperationDescription”/> of the operation.</param>

/// <param name=”ruleSet”>A <see cref=”String”/> naming the ruleset to be used.</param>

public ValidationParameterInspector(OperationDescription operation, string ruleSet)

{

// Check the synchronous method of the service first. If the operation implements the

// OperationContractAttribute.AsyncPattern property, set the method to the BeginMethod.

MethodInfo method = operation.SyncMethod;

if (method == null) method = operation.BeginMethod;

 

foreach (ParameterInfo param in method.GetParameters())

{

switch (param.Attributes)

{

case ParameterAttributes.Out:

case ParameterAttributes.Retval:

break;

default:

inputValidators.Add(CreateInputParameterValidator(param, ruleSet));

inputValidatorParameterNames.Add(param.Name);

break;

   }

}

}

 

/// <summary>

/// Performs the validations.

/// </summary>

/// <param name=”operationName”>A <see cref=”String”/> containing the name of the operation.</param>

/// <param name=”inputs”>

/// An <see cref=”object”/> array containing the input parameters of the method and expected as described on

/// the operation, and thus excluding input parameters resulting from the service’s implementation of the

/// <see cref=”OperationContractAttribute.AsyncPattern”/> property.

/// </param>

/// <returns>null</returns>

public object BeforeCall(string operationName, object[] inputs)

{

ValidationFault fault = new ValidationFault();

// Because there could be fewer inputs than inputValidators, iterate

// through the inputs to run the corresponding validations. This accounts

// for asynchronous methods where the callback and state are defined in the

// method but are not exposed outside the boundary of the service.

for (int i = 0; i < inputs.Length; ++i)

{

ValidationResults results = inputValidators[i].Validate(inputs[i]);

AddFaultDetails(fault, inputValidatorParameterNames[i], results);

}

 

if (!fault.IsValid)

{

   throw new FaultException<ValidationFault>(fault);

}

 

return null;

}

 

Changes to ValidationBehavior.cs

/// <summary>

/// Returns a <see cref=”Boolean”/> to indicate whether or not an operation has validation assertions.

/// </summary>

/// <remarks>

/// If the method implements the <see cref=”OperationContractAttribute.AsyncPattern”/> property,

/// the BeginMethod is used instead of the SyncMethod because the SyncMethod is null.

/// </remarks>

/// <param name=”operation”></param>

/// <returns></returns>

private bool HasValidationAssertions(OperationDescription operation)

{

if (operation == null) throw new ArgumentNullException(“operation”);

 

// Check the synchronous method of the service first. If the operation implements the

// OperationContractAttribute.AsyncPattern property, set the method to the BeginMethod.

MethodInfo methodInfo = operation.SyncMethod;

if (methodInfo == null) methodInfo = operation.BeginMethod;

 

return methodInfo.GetCustomAttributes(typeof(ValidatorAttribute), false).Length > 0 ||

   HasParametersWithValidationAssertions(methodInfo.GetParameters());

} 

 

I created a copy of this post on CodePlex.  See http://www.codeplex.com/entlib/Thread/View.aspx?ThreadId=31493.

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.

Create a free website or blog at WordPress.com.