Thursday 12 January 2012

WCF Request Validation


On a recent project that required the implementation of a WCF service, the purpose of which was to act as an interface for a third party to query and submit entities to our system over the internet, as usual with such integration the requirement to provide validation of the content of requests submitted to the service prior to execution of the request.

This validation takes the usual form of guarding against null values, empty collections, values out of range etc. In the past I would have traditionally written validation logic within the service implementation. This validation logic would then throw a fault to inform the caller of the data violations.

The requirements

I decided on this particular project to evaluate the other options available for WCF service request validations. Some of the issues of the traditional mechanism of service side validation that I wished to address where the following:
  1. Minimise the number lines of code for validation constructs
  2. Separate validation logic from the business logic
  3. Reduce the number and complexity of unit tests through exclusion of validation from unit testing
  4. Remove the need for weak entity attribute names from code. For example the common pattern of manual validation shown below
public void ProcessSomething(Something thing)
{
    if(thing == null)
    {
        var dataFault = new DataFault("thing cannot be null");

        throw new FaultException<DataFault>(dataFault, "Request validation error");
    }
}


In the above code snippet if the parameter name is modified without modifying he fault message, they may be located in different parts of code, the information conveyed by the fault is misleading to the caller.

The Options

From the relatively quick research I found the following alternatives available for WCF request validation:

  • Message schema validation, via message inspection, an example of such a strategy implementation is shown here 
  • IParameterInspector this in conjunction with some metadata could be used as the basis of a mechanism to inspect parameters on each operation invoke of a service 
  • Enterprise Library Validation Block this component provides a set of attributes or alternately application configuration metadata to define operation and data type validation rules

The Choice

When the three options are evaluated against the requirements outline above all of the options will address requirements 2, 3 and 4 at some level. Message schema validation and IParameterInspector are basically variations on a message inspection pattern and share the same disadvantages namely the amount of configuration plumbing code in the form of endpoint, service and operation behaviours and configuration elements that are required. As an exercise in understanding the internals of WCF and the extensiblity of the framework either implementing message schema validation or an IParameterInspector would be an interesting and educational task.

With the clock ticking on the project I decided that the Enterprise Library Validation Block was the most optimal solution give the time constraints and the requirements for what I wished to achive. The following where some of the specific advantages to me:

  • It reduces the number of LOC that I need to write for validation logic, it contains a comprehensive list of built in validators
  • It is straight forward to configure, I chose to use attribution on my service contracts for configuration
[ServiceContract]
[ValidationBehavior]
public interface IService
{
    [OperationContract]
    [FaultContract(typeof(ValidationFault))]
    void ProcessSomething(
        [NotNullValidator]
        Something thing);
}
  • Simplifies business logic as there is no longer a requirement to checks that are irrelevent to the business logic for example null checking. For example the validation attributes can enforce minimal values for Strings

[DataContract]
public class Something
{
    [DataMember]
    [NotNullValidator]
    [StringLengthValidator(0,10)]
    public String Name
    {
        get;
        set;
    }
}

// business logic can simply check the field value
// the validation attributes will ensured that the field 
// is not null by the time the business logic is invoked 
if(derivedSomething.Name == "Widget")
{
    OrderNewWidget();
}

  • The caller is returned a ValidationFault which contains a collection of ValidationDetail objects, which contains all of the validation violations for a given request
  • Custom validators are easy to implement and apply to code using the same mechanisms as with the built in validation attributes
There is one minor cavet when using the validation block, when a data contract type references another data contract type and a validation error occurs for the contained type, the ValidationDetail.Key for the validation error is always the type name of the parent data contract type.

To elaborate further take the following example data contract type Something that contains a nested Widget data contract type. The service comprises of an operation to process an instance of a Something type

[DataContract]
public class Something
{
    [DataMember]
    [NotNullValidator]
    [ObjectValidator]
    [ValidatorComposition(CompositionType.And)]
    public Widget ChildWidget
    {
        get;
        set;
    }
}

[DataContract]
public class Widget
{
    [DataMember]
    [NotNullValidator]
    public String Name
    {
        get;
        set;
    }
}

[ServiceBehavior]
public class Service : IService
{
    public void ProcessSomething(Something thing)
    {
    }
}

When the client invokes the service with a null value for Widget.Name it will recieve a ValidationFault whos Tag property refers to the containging types argument name on the service operation.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode xmlns="">s:Client</faultcode>
<faultstring xml:lang="en-IE" xmlns="">The creator of this fault did not specify a Reason.</faultstring>
<detail xmlns="">
<ValidationFault xmlns="http://www.microsoft.com/practices/EnterpriseLibrary/2007/01/wcf/validation" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Details xmlns:a="http://schemas.datacontract.org/2004/07/Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF">
<a:ValidationDetail>
<a:Key>Name</a:Key>
<a:Message>The value cannot be null.</a:Message>
<a:Tag>thing</a:Tag>
</a:ValidationDetail>
</Details>
</ValidationFault>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>


If both the Something type and the Widget type contained a Name propery then the information conveyed by the ValidationFault would be ambigous.

No comments:

Post a Comment