First off, why would you want to perform validation in the WCF layer?
After a vigorous PEN test, it was noted that the validation was mainly happening at the client browser and the application required validation to occur at the business logic layer, in our case the WCF layer.
In general, when working with an MVC/ASP.NET web application, you would typically want to do validation on the client-side as well as on the server side. Whilst the custom validation is simple enough, you’d have to duplicate it on the client and server, which is annoying – now you have two places to maintain a single validation routine.
Lets look at different validation options that will assist us in solving the issues over validation.
There are five validation approaches which you can prefer during validations. Each one has advantages and disadvantages over each other. Also, it is possible to apply multiple approaches at the same time. For example, you can implement self validation and data annotation attributes approaches at the same time, which gives you much flexibility.
- Rule sets in configuration
- Validation block attributes
- Data annotation attributes
- Self-validation
- Validators created programmatically
Rule sets in Configuration
In this approach, we put our validation rules into the configuration file (web.config in ASP.NET and app.config in Windows applications). Here is an example showing how to define validation rules:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <configSections> <section name="validation" type="Microsoft.Practices.EnterpriseLibrary. Validation.Configuration.ValidationSettings, Microsoft.Practices.EnterpriseLibrary.Validation, Version=5.0.414.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" requirePermission="true" /> </configSections> <validation> <type name="ELValidation.Entities.BasicCustomer" defaultRuleset="BasicCustomerValidationRules" assemblyName="ELValidation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"> <ruleset name="BasicCustomerValidationRules"> <properties> <property name="CustomerNo"> <validator type="Microsoft.Practices.EnterpriseLibrary. Validation.Validators.NotNullValidator, Microsoft.Practices.EnterpriseLibrary.Validation" negated="false" messageTemplate="Customer must have valid no" tag="CustomerNo" name="Not Null Validator" /> <validator type="Microsoft.Practices.EnterpriseLibrary. Validation.Validators.StringLengthValidator, Microsoft.Practices.EnterpriseLibrary.Validation" upperBound="5" lowerBound="5" lowerBoundType="Inclusive" upperBoundType="Inclusive" negated="false" messageTemplate="Customer no must have {3} characters." tag="CustomerNo" name="String Length Validator" /> <validator type="Microsoft.Practices.EnterpriseLibrary. Validation.Validators.RegexValidator, Microsoft.Practices.EnterpriseLibrary.Validation" pattern="[A-Z]{2}[0-9]{3}" options="None" patternResourceName="" patternResourceType="" messageTemplate="Customer no must be 2 capital letters and 3 numbers." messageTemplateResourceName="" messageTemplateResourceType="" tag="CustomerNo" name="Regex Validator" /> </property> </properties> </ruleset> </type> </validation> </configuration>
Validation Block Attributes
In this approach, we define our validations through the attributes defined in Enterprise Library validation block.
[NotNullValidator(MessageTemplate = "Customer must have valid no")] [StringLengthValidator(5, RangeBoundaryType.Inclusive, 5, RangeBoundaryType.Inclusive, MessageTemplate = "Customer no must have {3} characters.")] [RegexValidator("[A-Z]{2}[0-9]{3}", MessageTemplate = "Customer no must be 2 capital letters and 3 numbers.")] public string CustomerNo { get; set; }
Message template is a good way to provide meaningful messages on failure with the flexibility to be replaced by Enterprise Library validation block for brackets.
Data Annotation Attributes
In this approach, we define our validations through the attributes defined within System.ComponentModel.DataAnnotations assembly.
[Required(ErrorMessage = "Customer no can not be empty")] [StringLength(5, ErrorMessage = "Customer no must be 5 characters.")] [RegularExpression("[A-Z]{2}[0-9]{3}", ErrorMessage = "Customer no must be 2 capital letters and 3 numbers.")] public string CustomerNo { get; set; }
This approach is widely used in conjunction with Entity Framework, MVC and ASP.NET validations.
Self-validation
This approach gives much flexibility to us in order to create and execute complex validation rules.
In order to implement this approach, we first decorate HasSelfValidation attribute with the object type as shown in the following example:
[HasSelfValidation] public class AttributeCustomer { … }
Then, we write our validation logic by putting SelfValidation attribute on the top of the method which executes the validations.
[SelfValidation] public void Validate(ValidationResults validationResults) { var age = DateTime.Now.Year - DateTime.Parse(BirthDate).Year; // Due to laws, only customers older than 18 can be registered // to system and allowed to order products if (age < 18) { validationResults.AddResult( new ValidationResult("Customer must be older than 18", this, "BirthDate", null, null)); } }
Validators Created Programmatically
This approach is different from the others because validation rules are created programmatically and executed independent of the type.
First, we define our validation rules:
Validator[] validators = new Validator[] { new NotNullValidator(false, "Value can not be NULL."), new StringLengthValidator(5, RangeBoundaryType.Inclusive, 5, RangeBoundaryType.Inclusive, "Value must be between {3} and {5} chars.") };
Then, we add them into one of the composite validators depending on your preference.
var validator = new AndCompositeValidator(validators);
In this example, we check Value to be tested… if it is not null and it has five exact characters.
Finally, I want to mention about the validations against collections. Actually, it is similar to the validations for objects.
// Initialize our object and set the values var customer = new AttributeCustomer(); FillCustomerInfo(customer); // Create a list of objects and add the objects to be tested to the list List<AttributeCustomer> customers = new List<AttributeCustomer>(); customers.Add(customer); // Initialize our validator by providing the type of objects in the list and validate them Validator cusValidator = new ObjectCollectionValidator(typeof(AttributeCustomer)); ValidationResults valResults = cusValidator.Validate(customers); // Show our validation results ShowResults(valResults);
First thought was to use the MVC answer to validation – DataAnnotations, so why can’t we do this?
WCF is technology for exposing services and it does it in interoperable way. Data contract exposed by the service is just create for data. It doesn’t matter how many fancy attributes you use on the contract or how many custom logic you put inside get and set
methods of the property. On the client side you always see just the properties.
The reason for this is that once you expose the service it exposes all its contracts in interoperable way – service and operation contracts are described by WSDL and data contracts are described by XSD. XSD can describe only structure of data but no logic. The validation itself can be in some limited way be described in XSD but .NET XSD generator doesn’t do this. Once you add service reference to your WCF service the proxy generator will take WSDL and XSD as a source and create your classes again without all that attributes.
If you want to have client side validation you should in the first place implement that validation on the client side – it can be done by using buddy classes for partial classes used by WCF proxy. If you want to have a maintenance nightmare and you don’t want to use this way you must share assembly with your entities between your WCF client and WCF service and reuse those types when adding service reference. This will create tight coupling between your service and ASP.NET MVC application.
What about Microsoft.Practices.EnterpriseLibrary.Validation.Validators?
This is a possible solution to the WCF layer, but what this does not provide is a process to pass the validation back to the UI for JavaScript validation, which you get from DataAnnotations.
It may be worth using both the DataAnnotations and the Enterprise Library validation block together.
One question still to remain, is if the validation fails in the Services layer (WCF) how does the validation message get passed to the calling WCF client?
In general, when working with an MVC/ASP.NET web application, you would typically want to do validation on the client-side as well as on the server side. Whilst the custom validation is simple enough, you’d have to duplicate it on the client and server, which is annoying – now you have two places to maintain a single validation routine.
So what else can we use?
First of all, do not throw Exceptions as a way of validating data – that’s a way too expensive operation, instead of graceful handling of invalid data.
If you would like to see some sample code take a look at Microsoft Enterprise Library 5.0 – Introduction to Validation Block by Ercan Anlama
ELValidation_src.zip (772.17 kb)
We’ve looked at all possible validation options, now the searching question is how do pass the validation over WCF to the WCF client for validation? to be continued……