How to: Validate Data
This article is relevant to entity models that utilize the deprecated Visual Studio integration of Telerik Data Access. The current documentation of the Data Access framework is available here.
Input and data validation represents one important aspect of your WCF application. WCF provides extensibility points that allow you to customize the WCF runtime behavior by creating custom extensions. Message and Parameter Inspectors are two extensibility mechanisms that give you greater control over the data passing between a client and a service. You need to use parameter inspectors for input validation and use message inspectors only when you should inspect the entire message.
This article demonstrates how to perform input and data validation on parameters in WCF operations. The topic shows you how to create custom parameter inspectors that can be used for data validation both on the server and the client.
Input Validation
To perform input validation, you need to implement a custom parameter inspector. This is the main purpose of the parameter inspectors, i.e. to validate parameters exposed in WCF service operations. Although it is quite possible to perform the validation inside the service's method, the downside is that messages containing invalid parameters have to make a trip from the client to the service. To create a parameter inspector, you need to implement the IParameterInspector interface. This interface exposes two methods: BeforeCall and AfterCall. The parameters sent to the method are presented as an array of objects.
Creating Parameter Inspector
The following example demonstrates how to create a class that implements the IParameterInspector interface. The purpose of the class is to validate a car object. The BeforeCall method is invoked just before the service call. The AfterCall method is called after the service response. The first parameter in the BeforeCall method is the operationName. This parameter provides information about which operation is being called. The operationName is the name of the operation as it is exposed in the contract. This might not be the same as the method called on the client proxy object. Another important thing is the return value from the BeforeCall method. The value returned from a BeforeCall method will be passed into the corresponding AfterCall method. The idea here is to correlate the BeforeCall and AfterCall methods.
The code snippet below shows sample validation logic to check if the car parameter passed to the operation is valid. If the validation fails, an exception is thrown with error message.
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using WebApplication.Dto;
namespace WebApplication
{
public class CarInspector : IParameterInspector
{
public object BeforeCall( string operationName, object[] inputs )
{
if ( operationName == "CreateCar" )
{
CarDto car = inputs.FirstOrDefault() as CarDto;
if ( car != null &&
( string.IsNullOrWhiteSpace( car.Make ) ||
string.IsNullOrWhiteSpace( car.Model ) ) )
{
throw new FaultException<string>( "Invalid car." );
}
}
return null;
}
public void AfterCall( string operationName, object[] outputs, object returnValue,
object correlationState )
{
if ( operationName == "CreateCar" )
{
CarDto car = outputs.FirstOrDefault() as CarDto;
if ( car != null &&
( string.IsNullOrWhiteSpace( car.Make ) ||
string.IsNullOrWhiteSpace( car.Model ) ) )
{
throw new FaultException<string>( "Invalid car." );
}
}
}
}
}
Imports System.ServiceModel
Imports System.ServiceModel.Dispatcher
Imports WebApplication.Dto
Public Class CarInspector
Implements IParameterInspector
Public Function BeforeCall(ByVal operationName As String,
ByVal inputs() As Object) As Object _
Implements IParameterInspector.BeforeCall
If operationName = "CreateCar" Then
Dim car As CarDto = TryCast(inputs.FirstOrDefault(), CarDto)
If car IsNot Nothing AndAlso (String.IsNullOrWhiteSpace(car.Make) OrElse _
String.IsNullOrWhiteSpace(car.Model)) Then
Throw New FaultException(Of String)("Invalid car.")
End If
End If
Return Nothing
End Function
Public Sub AfterCall(ByVal operationName As String,
ByVal outputs() As Object,
ByVal returnValue As Object, ByVal correlationState As Object) _
Implements IParameterInspector.AfterCall
If operationName = "CreateCar" Then
Dim car As CarDto = TryCast(outputs.FirstOrDefault(), CarDto)
If car IsNot Nothing AndAlso (String.IsNullOrWhiteSpace(car.Make) OrElse _
String.IsNullOrWhiteSpace(car.Model)) Then
Throw New FaultException(Of String)("Invalid car.")
End If
End If
End Sub
End Class
Creating Custom Endpoint Behavior
The endpoint behavior allows you to add a parameter inspector to either the client or the service side of the WCF pipeline. In general, you need to do some coding to accomplish this task. You must create a class that implements the IEndpointBehavior interface. The IEndpointBehavior interface exposes four methods:
- AddBindingParameters - adds the parameters required by this behavior to the binding.
- Validate - performs endpoint validation to ensure that the applied behavior is supported.
- ApplyDispatchBehavior - allows you to customize the endpoint on the service side of the request.
- ApplyClientBehavior - allows you to customize the endpoint on the client side of the request.
The two methods that are likely to be of interest are ApplyDispatchBehavior and ApplyClientBehavior.
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
namespace WebApplication
{
public class CarValidationBehavior : IEndpointBehavior
{
internal CarValidationBehavior( bool enabled )
{
this.Enabled = enabled;
}
public bool Enabled
{
get;
set;
}
public void Validate( ServiceEndpoint endpoint )
{
}
public void AddBindingParameters( ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters )
{
}
public void ApplyDispatchBehavior( ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher )
{
if ( this.Enabled == false )
{
return;
}
foreach ( DispatchOperation dispatchOperation in
endpointDispatcher.DispatchRuntime.Operations )
{
dispatchOperation.ParameterInspectors.Add( new CarInspector() );
}
}
public void ApplyClientBehavior( ServiceEndpoint endpoint, ClientRuntime clientRuntime )
{
if ( this.Enabled == false )
{
return;
}
foreach ( ClientOperation clientOperation in clientRuntime.Operations )
{
clientOperation.ParameterInspectors.Add( new CarInspector() );
}
}
}
}
Imports System.ServiceModel.Dispatcher
Imports System.ServiceModel.Description
Imports System.ServiceModel.Channels
Public Class CarValidationBehavior
Implements IEndpointBehavior
Friend Sub New(ByVal enabled As Boolean)
Me.Enabled = enabled
End Sub
Public Property Enabled() As Boolean
Public Sub Validate(ByVal endpoint As ServiceEndpoint) _
Implements IEndpointBehavior.Validate
End Sub
Public Sub AddBindingParameters(ByVal endpoint As ServiceEndpoint,
ByVal bindingParameters As BindingParameterCollection) _
Implements IEndpointBehavior.AddBindingParameters
End Sub
Public Sub ApplyDispatchBehavior(ByVal endpoint As ServiceEndpoint,
ByVal _endpointDispatcher As EndpointDispatcher) _
Implements IEndpointBehavior.ApplyDispatchBehavior
If Me.Enabled = False Then
Return
End If
For Each _dispatchOperation As DispatchOperation In _
_endpointDispatcher.DispatchRuntime.Operations
_dispatchOperation.ParameterInspectors.Add(New CarInspector())
Next _dispatchOperation
End Sub
Public Sub ApplyClientBehavior(ByVal endpoint As ServiceEndpoint,
ByVal _clientRuntime As ClientRuntime) _
Implements IEndpointBehavior.ApplyClientBehavior
If Me.Enabled = False Then
Return
End If
For Each _clientOperation As ClientOperation In _clientRuntime.Operations
_clientOperation.ParameterInspectors.Add(New CarInspector())
Next _clientOperation
End Sub
End Class
Creating Custom Configuration Element
In this step, you create a new class, derived from BehaviorExtensionElement that implements a custom configuration element. You need to add the custom endpoint behavior to the behavior extension. After the behavior is added to such an extension, the next step is to add it to the WCF pipeline through the configuration file. The minimum functionality for the behavior extension is to override the CreateBehavior method so that it returns an instance of the desired behavior. The following code demonstrates a sample behavior extension implementation. Note the ConfigurationProperty attribute. It allows the behavior to be enabled or disabled in the WCF configuration files.
using System;
using System.Configuration;
using System.ServiceModel.Configuration;
namespace WebApplication
{
public class CustomBehaviorSection : BehaviorExtensionElement
{
private const string EnabledAttributeName = "enabled";
public CustomBehaviorSection()
{
this.Enabled = true;
}
[ConfigurationProperty( EnabledAttributeName, DefaultValue = true, IsRequired = false )]
public bool Enabled
{
get;
set;
}
protected override object CreateBehavior()
{
return new CarValidationBehavior( this.Enabled );
}
public override Type BehaviorType
{
get
{
return typeof( CarValidationBehavior );
}
}
}
}
Imports System.ServiceModel.Configuration
Public Class CustomBehaviorSection
Inherits BehaviorExtensionElement
Private Const EnabledAttributeName As String = "enabled"
Public Sub New()
Me.Enabled = True
End Sub
<ConfigurationProperty(EnabledAttributeName, DefaultValue:=True, IsRequired:=False)>
Public Property Enabled() As Boolean
Protected Overrides Function CreateBehavior() As Object
Return New CarValidationBehavior(Me.Enabled)
End Function
Public Overrides ReadOnly Property BehaviorType() As Type
Get
Return GetType(CarValidationBehavior)
End Get
End Property
End Class
Adding the Custom Behavior to the Configuration File
After the behavior extension is implemented, you need to add it to the behavior element extension in the WCF configuration file so that it can be used by the WCF endpoint.
- In the web project containing your wcf service, right-click the web.config file and then select Edit WCF Configuration. If you do not see the Edit WCF Configuration option, on the Tools menu, click WCF Service Configuration Editor. Close the WCF Service Configuration Editor tool that appears. The option should now appear on the web.config context menu.
- Expand the Advanced node and the Extensions node, and then click behavior element extensions.
- Click New.
- In the Name field, type Validator.
- Select the Type field, click the button that appears to the right, navigate to the folder containing the assembly with the inspector class, and then double-click the dll file.
- Double-click the type name WebApplication.CustomBehaviorSection and then click OK. "WebApplication" is the namespace of the custom configuration element.
- In the WCF Configuration Editor, on the File menu, click Save.
Verify that your configuration file contains the following:
<system.serviceModel>
<!--****-->
<extensions>
<behaviorExtensions>
<add name="Validator" type="WebApplication.CustomBehaviorSection, WebApplication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<!--****-->
</system.serviceModel>
Creating an Endpoint Behavior and Mapping it to Use the Custom Behavior
Next, you create an endpoint behavior and map it to the custom behavior created in step 2:
- In the WCF Configuration Editor, expand the Advanced node, right-click Endpoint Behavior, and then click New Endpoint Behavior Configuration.
- Select the new behavior and then in the Name field, type MyEndPointBehavior.
- Click Add, select the Validator custom behavior, and then click Add.
- In the WCF Configuration Editor, on the File menu, click Save.
Verify that your configuration file contains the following:
<endpoint address="" behaviorConfiguration="MyEndPointBehavior"
binding="basicHttpBinding" contract="WebApplication.IEntitiesModelService">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
Testing the Parameter Validator
The final step is to add a service reference in the client project and test the target method (e.g. CreateCar).