DevTrends

Validating WCF service operations using System.ComponentModel.DataAnnotations

All WCF services operations need some level of parameter validation to ensure that the data passed to the operation is present and correct. Two popular methods are manual checking and the Validation Application Block (VAB) from Enterprise Library. This article discusses another option - using the validation features from System.ComponentModel.DataAnnotations.

This article was heavily inspired by a recent post by Thoai Nguyen.

Most WCF service operations take in one or more parameters and in almost all cases, you will want some sort of validation of these parameters. This validation could be something as simple as a null check or something a bit more sophisticated. There are many different methods you can use to achieve this. The simplest approach is to perform the validation at the start of each operation:

if (authenticateRequest == null)
{
    throw new ArgumentNullException("authenticateRequest");
}

if (authenticateRequest.Username == null)
{
    throw new ArgumentException("authenticateRequest.Username cannot be null");
}

This approach is very transparent but depending on the complexity of the request and the amount of validation required, it can seriously affect the readability of the operation. There may also be duplication issues.

Another option that is very popular is to use the Validation Application Block (VAB) from Microsoft's Enterprise Library. VAB includes numerous attributes that you decorate your datacontracts with and a WCF behavior that enables all the magic.

[ValidationBehavior]
public interface IMembershipService
{
  [OperationContract]
  [FaultContract(typeof(ValidationFault))]
  AuthenticateResponse Authenticate(AuthenticateRequest authenticateRequest);

  ...
}

[NotNullValidator]
public class AuthenticateRequest
{
  [DataMember]
  [NotNullValidator]
  public string Username { get; set; }

  ...
}

The VAB approach is very comprehensive and I have used it successfully on several projects but I do have some problems with it. Firstly, it requires that your data and service contracts have a reference to the following EntLib DLLs:

At the service level this is OK, but for internal services, it is very common to share the contract assembly with the client, meaning that the client also needs to reference these assemblies.

The other problem that I have with VAB is the FaultException that is raised when validation fails. The structured ValidationFault that VAB throws is very useful if you want to explicitly catch and deal with validation errors from the server, but this is not what I want for the majority of my non-public facing services. As far as I am concerned, if an invalid call (a call which fails service validation) is made to the service, then there is a bug in the client. It should not be possible to make an invalid call because all clients should validate user input sufficiently. I never want to write client code to deal with these validation failures specifically, so have no use for a structured exception.

Having said that, receiving this structured information in addition to a readable error message would be fine but unfortunately, with VAB this is not the case. This is what we get when debugging through Visual Studio:

Visual Studio VAB Unfriendly Error Message

Figure 1: Visual Studio VAB Unfriendly Error Message

If you have exception logging code, then unless you are doing something special, you will end up logging "The creator of this fault did not specify a Reason" and the actual problem(s) will not even be recorded. Similarly, if you use the WCF Test Client to pass invalid data to a WCF service using VAB, you get this extremely unhelpful message:

Validation Application Block Unfriendly Error Message

Figure 2: Validation Application Block Unfriendly Error Message

Yes, I can go into the SOAP XML and try and find out the actual error but it is not the behaviour that I want. I just want a simple message indicating exactly what is wrong with my request. In case you hadn't already guessed, I find this VAB quirk a little annoying.

Why choose DataAnnotations?

So as we have seen, I am not 100% happy with the Validation Application Block or manual validation but I certainly don't want to go to the trouble of implementing an entire validation framework from scratch. A problem ... with a simple solution. Enter System.ComponentModel.DataAnnotations.

The System.ComponentModel.DataAnnotations namespace is well established and reasonably well-known thanks to its use in ASP.NET MVC, WPF, Silverlight et al. The assembly is part of the .NET framework, so no external assembly references are required. It is also very capable and easily customisable. In conclusion, it is ideal for our needs.

So a fantastic validation framework is available with .NET. All we need to do is integrate it into WCF.

Hooking into each WCF operation

WCF has an excellent extensibility model so hooking in to most aspects of WCF is possible. Runtime extensibility is usually achieved using behaviors, of which there are four types: service, endpoint, contract and operation. I think it makes most sense to apply our validation behavior to the actual service, so all operations get validated without needing to explicitly include each one. Therefore our behavior will need to implement IServiceBehavior.

The next question that we need to answer is how to get access to the parameters for each operation. Thanks to a great article by Thoai Nguyen, I now know about the IParameterInspector interface which is exactly what we need.

public interface IParameterInspector
{
  object BeforeCall(string operationName, object[] inputs);
  void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState);
}

Essentially, we need to use a WCF behavior to add a parameter inspector to each operation. In the BeforeCall method of the parameter inspector, we need to cycle through all inputs and validate them. If there are any validation failures, we throw a FaultException with the details.

Integrating DataAnnotations into WCF

Note that I am not going to explain every detail of the package, so if you are not sure about something, please take a look at the source code on CodePlex which is very readable and comes complete with unit tests.

To begin, we create the behavior class and inplement IServiceBehavior. In the ApplyDispatchBehavior method of our behavior, we enumerate over all operations and then add our ValidatingParameterInspector instance to each one.

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
    var operations =
        from dispatcher in serviceHostBase.ChannelDispatchers.Cast<ChannelDispatcher>()
        from endpoint in dispatcher.Endpoints
        from operation in endpoint.DispatchRuntime.Operations
        select operation;

    operations.ToList().ForEach(operation => operation.ParameterInspectors.Add(_validatingParameterInspector));
}

Now we come to actually defining how and what we are going to validate. The System.ComponentModel.DataAnnotations namespace is probably most widely known as the home of numerous validation attributes. These includes [Required], [RegularExpression] and [Range] plus several others. We also have an abstract ValidationAttribute class that can be used to write your own validation attributes very easily. If you are a regular reader of this blog, you will remember that we talked about writing custom validation attributes in the context of ASP.NET MVC3 in a previous article. We are definitely going to have to support both built-in and custom attributes but as we will see, this is very easy.

Sometimes, for ad hoc validation requirements, you might not want to go to the trouble of subclassing ValidationAttribute. For these situations, DataAnnotations provides another option - IValidatableObject. Again, we have discussed this interface previously. In the context of ASP.NET MVC, we concluded that it was not particularly useful due to its inability to perform client-side validation. In WCF however, it is much more usable and it makes sense that we support it. Instead of using attributes, it involves implementing a single Validate method:

IEnumerable<ValidationResult> Validate(ValidationContext validationContext);

We are also going to add a third level of validation to check for null arguments. Please note that this code assumes that you are using request/response type contracts rather than RPC-style simple types. I believe that most people (certainly at enterprise level) will be implementing their services in this manner.

Given the fact that there are three types of validation, it makes sense to define a validator interface that we can use to decouple our code. We can then inject a collection of validators into our parameter inspector. We should also probably abstract out the error message generation and inject that too.

public interface IObjectValidator
{
    IEnumerable<ValidationResult> Validate(object input);
}

public class ValidatingParameterInspector: IParameterInspector
{
    private readonly IEnumerable<IObjectValidator> _validators;
    private readonly IErrorMessageGenerator _errorMessageGenerator;

    public ValidatingParameterInspector(IEnumerable<IObjectValidator> validators, IErrorMessageGenerator errorMessageGenerator)
    {
        ValidateArguments(validators, errorMessageGenerator);

        _validators = validators;
        _errorMessageGenerator = errorMessageGenerator;
    }

    ...
}

With these abstractions in place, our parameter inspector implementation is trivial. The BeforeCall method is shown below:

public object BeforeCall(string operationName, object[] inputs)
{
    var validationResults = new List<ValidationResult>();

    foreach (var input in inputs)
    {
        foreach (var validator in _validators)
        {
            var results = validator.Validate(input);
            validationResults.AddRange(results);                    
        }                                
    }

    if (validationResults.Count > 0)
    {
        throw new FaultException(_errorMessageGenerator.GenerateErrorMessage(operationName, validationResults));
    }

    return null;
}

Now let's look at each object validator. Starting with the NullCheckObjectValidator which needs no explanation:

public class NullCheckObjectValidator : IObjectValidator
{
    public IEnumerable<ValidationResult> Validate(object input)
    {
        if (input == null)
        {
            yield return new ValidationResult("Input is null.");                
        }            
    }
}

The DataAnnotationsObjectValidator uses reflection to query all ValidationAttributes from all properties. We call the IsValid method on each validator, passing in the property value. For all invalid properties, we return a validation result using the error message defined in the validation attribute.

public class DataAnnotationsObjectValidator : IObjectValidator
{
    public IEnumerable<ValidationResult> Validate(object input)
    {
        if (input == null) return Enumerable.Empty<ValidationResult>();

        return from property in TypeDescriptor.GetProperties(input).Cast<PropertyDescriptor>()
               from attribute in property.Attributes.OfType<ValidationAttribute>()
               where !attribute.IsValid(property.GetValue(input))
               select new ValidationResult
               (
                   attribute.FormatErrorMessage(string.Empty),
                   new[] { property.Name }
               );
    }
}

Now, we move on to support for IValidatableObject. As we saw earlier, IValidatableObject contains a single validate method which should be fairly easy for us to call:

public class ValidatableObjectValidator : IObjectValidator
{
    public IEnumerable<ValidationResult> Validate(object input)
    {
        var validatableInput = input as IValidatableObject;

        if (validatableInput != null)
        {
            return validatableInput.Validate(new ValidationContext(input, null, null));
        }

        return Enumerable.Empty<ValidationResult>();
    }
}

Again very simple. We try and cast the input to IValidatableObject and if successful, we call the Validate method with a ValidationContext and return the results. If we find that the input does not implement IValidatableObject, we return an empty enumeration.

Finally, if any validation errors have been found in the three previous steps, we generate a readable error message to be thrown as a fault exception. We could return a structured fault exception here but as mentioned previously, I do not want to write code to handle these kinds of exceptions at the client. When debugging, I want the client to break immediately. When released, I want to handle the exception as any other unforeseen exception - typically logging it and showing the user a friendly error message. In both cases, a structured error would not add value. If you disagree, feel free to modify it to suit your requirements.

public class ErrorMessageGenerator : IErrorMessageGenerator
{
    public string GenerateErrorMessage(string operationName, IEnumerable<ValidationResult> validationResults)
    {
        ValidateArguments(operationName, validationResults);

        var errorMessageBuilder = new StringBuilder();

        errorMessageBuilder.AppendFormat("Service operation {0} failed due to validation errors: \n\n", operationName);

        foreach (var validationResult in validationResults)
        {
            var memberName = validationResult.MemberNames.FirstOrDefault();

            if (memberName == null)
            {
                errorMessageBuilder.AppendFormat("{0} \n", validationResult.ErrorMessage);
            }
            else
            {
                errorMessageBuilder.AppendFormat("{0}: {1} \n", memberName, validationResult.ErrorMessage);
            }
        }

        return errorMessageBuilder.ToString();
    }
}

Using the ValidateDataAnnotationsAttribute

(1) Install the NuGet package using the Install-Package WCFDataAnnotations command. Alternatively, download the dll from CodePlex and manually add a reference to DevTrends.WCFDataAnnotations.dll in the project containing your service implementation.

(2) Decorate your service implementation with the ValidateDataAnnotationsBehavior attribute:

[ValidateDataAnnotationsBehavior]
public class MembershipService : IMembershipService 

If you prefer, instead of using the attribute, you can add the behavior in configuration:

<system.serviceModel>
  <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
  <extensions>
    <behaviorExtensions>
      <add name="validateDataAnnotationsBehavior" 
           type="DevTrends.WCFDataAnnotations.ValidateDataAnnotationsBehaviorExtensionElement, DevTrends.WCFDataAnnotations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </behaviorExtensions>
  </extensions>
  <behaviors>
    <serviceBehaviors>
      <behavior>
        <validateDataAnnotationsBehavior/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <services>
    </service>...</services>
  <bindings>...</bindings>
  <client>...</client>
</system.serviceModel>

(3) Add a reference to the .NET 4.0 assembly System.ComponentModel.DataAnnotations in the project containing your data contracts.

(4) Decorate your data contracts with the relevant data annotations.

[DataContract(Namespace = "http://schemas.devtrends.co.uk/example/data")]
public class AddCustomerAffiliationRequest
{
    [DataMember]
    [Required]
    public string ThirdPartyIdentifier { get; set; }       

    [DataMember]
    [Required]
    [StringLength(500, MinimumLength = 5)]
    public string Description{ get; set; }

    ... 
}

You can also/instead implement IValidatableObject:

[DataContract(Namespace = "http://schemas.devtrends.co.uk/example/data")]
public class AddCustomerToPromotionRequest : IValidatableObject
{
    [DataMember]
    public int PromotionId { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (PromotionId < 1)
        {
            yield return new ValidationResult("PromotionId must be greater than 0", new[] { "PromotionId" });
        }
    }
}

That's it. Connect to your WCF service and your operation arguments will automatically be validated using your configured attributes. If you try to pass invalid data, a FaultException will be thrown detailing the validation failure(s).

WCFDataAnnotations Friendly Error Message

Figure 3: WCFDataAnnotations Friendly Error Message

Conclusion

The Validation Application Block has some issues that I find rather annoying - the many one being the lack of a fault reason in the generic FaultException, making logging and debugging unnecessarily difficult. Fortunately, it is extremely easy to roll your own validation that hooks into the WCF pipeline. We would certainly not want to start from scratch but we do not need to - thanks to the excellent validation features in System.ComponentModel.DataAnnotations which are already well-known to many .NET developers.

The WCFDataAnnotations Nuget package is now available. The full source code is available on CodePlex should you need it. WCFDataAnnotations allows you to decorate your WCF service contracts with validation attributes and have each operation validate its inputs using these attributes automatically. Custom validation attributes and IValidatableObject are also supported. Hope you find it useful.