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:
- Microsoft.Practices.EnterpriseLibrary.Validation.dll
- Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF.dll
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:
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:
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 implement 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
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).
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.
Very cool! I recently had to do something similar, but my solution wasn't as elegant as this. Nice job!
Thank you for your great work.
BTW I am trying to apply wcfdataannotations on my WCF service which my jquery ajax invoke. The trouble I have is that I do not know where should I catch the exception.
My service is as follow :
public void AddUser(User user)
{
try
{
using (GolfJinDbContext db = new GolfJinDbContext())
{
db.Users.Add(user);
db.SaveChanges();
}
}
catch (Exception ex)
{
logger.Error("adduser {0}", ex.Message);
}
}
I expected that try-catch would catch the validation error, but prior to execution of AddUser method, validation error is already thrown so that I got "400 Bad Request" error.
To sum up, What should I do to treat validation error on my service method?
@Ray - the validation occurs before the operation is executed so you need to catch it a little earlier in the pipeline. I use IErrorHandler and log the error in the HandleError method.
Great package!
I don't know if the mechanism exists to do this, but it's a bit difficult to present a user friendly error message.
It doesn't look like FaultException is sealed - however I'm not sure if you could extend it and return a list of ValidationResults or anything. Right now you have to do some string jokeying to get it right.
Thanks!
@Adam - as I mentioned in the article, I am advising that your primary validation mechanism should reside in the client. In my mind, it is not possible to centralise your validation in the service whilst also providing the best user experience (such as immediate JS validation). In an MVC client, you would typically use data annotations on your view models which would allow javascript and server side (mvc) validation, providing friendly error messages to your users in a optimal way.
I see the validation on the service to be more of a backup. The client should never be allowed to make calls to the service with the kind of invalid data that can be picked up by data annotations. If a call is made, then it is a bug. Therefore, there is no need for structured errors as they will never be seen or used by client code. When debugging, the developer will see the error immediately and fix the bug and when in production, the error will be caught and logged and the user will see a generic friendly error message.
The standard way to return structured errors from WCF is the generic version of the FaultException class but in order for the client to use these, a fault contract needs to be created for each operation. This adds a dependency on the WCFDataAnnotations package to the contracts assembly which as outlined above, is not ideal for situations where the contracts assembly is shared between server and client.
Having said all that, I can see situations where structured errors may be more useful - smart clients for example. If it does not have a detrimental affect on the approach mentioned above, I will try and include it in the next release.
In the meantime, as this is the mechanism used by the Validation Application Block (VAB), if you do want structured errors right now, you can either modify the WCFDataAnnotations source code yourself (available on CodePlex) or use the VAB.
Very handy project!
I am having a problem though as I have an operation contract like below where I havent used a marked up data contract just a simple nullable int.
OrderDTO StartNewOrder(int? profileId)
In this case null is valid but I get an 'Input is null' validation exception. Is there anything I can do?
@Matt - There is nothing you can do without modifying the source. I am a strong believer in using data contracts for every operation so I have enforced this in the package. You could remove the NullCheckObjectValidator and recompile if you do not require it though.
Hi,
Thanks for a great article.
Anyhow, I am having problem using this approach with array of data-objects for instance
DataContract:
[DataContract]
public class Data
{
[StringLength(10)]
public string Prop1{get;set;}
}
Service Contract:
[ServiceContract]
[ValidateDataAnnotationsBehavior]
class Service1
{
public void Method1(List<Data> listOfData)
{
...
...
}
}
As you can see i have a constrain on the property, Prop1, and since the DataAnnotationsObjectValidator does not check for parameters of type List/Array/IEnumerable the constrain does not applied.
Is my observe correct or do i misse something?
Please instruct!.
In advance thanks.
/NTN
Hi Paul,
For validating a collection of objects, I have to change the DataAnnotationsObjectValidator and I post it here if you or anyone else can verify it or perhaps make it more robust.
Again thanks for a great article.
public class DataAnnotationsObjectValidator : IObjectValidator
{
public IEnumerable<ValidationResult> Validate(object input)
{
if (input == null) return Enumerable.Empty<ValidationResult>();
if (!IsCollection(input))
return SimpleObject(input);
return IEnumerableObject(input);
}
public bool IsCollection(object o)
{
return typeof(ICollection).IsAssignableFrom(o.GetType())
|| typeof(ICollection<>).IsAssignableFrom(o.GetType());
}
private IEnumerable<ValidationResult> SimpleObject(object input)
{
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 }
);
}
private IEnumerable<ValidationResult> IEnumerableObject(object input)
{
List<ValidationResult> validateResults = new List<ValidationResult>();
foreach (object obj in (input as IEnumerable))
{
validateResults.AddRange(Validate(obj));
}
return validateResults as IEnumerable<ValidationResult>;
}
}
/NTN
@NTN - Thanks for your work. Very useful. Will look at incorporating this plus support for nested complex types in the next release.
Is that works in Silverlight solution?
if so, i don't know why that haven't take any effect in my solution;
[ServiceContract]
public interface IWCFService
{
[OperationContract]
void DoWork();
[OperationContract]
Person GetPerson();
}
[DataContract(Namespace = "http://schemas.devtrends.co.uk/example/data")]
public class Person
{
[DataMember]
[Required]
public string ThirdPartyIdentifier { get; set; }
[DataMember]
[Required]
[StringLength(500, MinimumLength = 5)]
public string Description { get; set; }
}
these are the serivce implement class and the entity class.
and this is a silverlight 4 project。
can anyone tell me the reason.
I have a propoerty on my contract of type:
public List<RewardDetail> ShoppingCart { get; set; }
however the nested list does not validate. Even with the patch it does not inherit directly.
Any ideas?
Nice post by and nice hack by Ngoc
To the last comment:
Do use List<RewardDetail> use RewardDetail[ ]
Change NTN's method:
public bool IsCollection(object o)
{
return typeof(ICollection).IsAssignableFrom(o.GetType())
|| typeof(ICollection<>).IsAssignableFrom(o.GetType());
}
To:
public bool IsCollection(object o)
{
return typeof(ICollection<>).IsAssignableFrom(o.GetType())
|| typeof(ICollection<>).IsAssignableFrom(o.GetType())
|| o.GetType().IsArray;
}
The last line is the difference. It worked for me.