Tech Blog

Validating Schemas in WebMethods using Attributes

One of the most annoying things in BizTalk is when an error is generated in a Receive Pipeline indicating that a message has been suspended because of the content of the message doesn’t validate against the given schema (for the moment, we’ll gloss over the fact that in an ideal situation this should only ever happen during testing).

Using BizTalk 2006’s new Failed
Message Routing
feature, it’s fairly simple to write something which can deal
with this situation (using either an Orchestration or SendPort subscribing to the
Error Report).

Returning a message back to a Web Service caller indicating that there is something
wrong with their message, is non-trivial – it’s possible, but requires some in-depth
knowledge of how instance subscriptions work.

However, there is an easier way: validating the message at the endpoint (i.e. inside
the Web Service itself).
Aaron Skonnard and
Dan Sullivan wrote an article for MSDN magazine in July 2003 on how
to implement attribute based schema validation for Web Method parameters
.

It’s a great idea: decorate a Web Method with an attribute, and when the web method
is called then any messages received will be validated against their schemas – if
they don’t validate, then a SOAP Fault is returned back to the caller.

Looking through the code, I realised that with a few modifications it could work for
my situation.

The main issue with the code as it was is that, by default, it would validate incoming
XML messages against the XSD contained in the WSDL.

If you’re using the BizTalk Web Services Publishing Wizard, you’re probably
letting the tool do all the code generation for you. This also means that you’re probably
letting the .NET Framework look after generating your WSDL for you.

There’s a problem with auto-generation (as an aside, auto-generation might make your
life easier, but it can cause all sorts of problems if you’re not aware of how it
works, and what “default scenarios” it assumes).

Here’s what most people would do:

  • Create a schema in BizTalk in represent a message.
  • Use the Web Services Publishing Wizard to create a Web Service which can accept message
    using this schema.
    During this process, the wizard will use the xsd.exe tool
    to auto-generate a class (usually a C# class) which represents the XSD.
  • A caller then requests the WSDL for the web service – and the auto-generated class
    is converted into an XSD for inclusion in the WSDL.

However, a C# class can’t fully represent an XML schema (and vice versa). Anyone who’s
worked with complex (real-world) schemas will have realized this.

One area where the whole thing falls over is when we put restrictions on attributes
or elements.

For example, suppose we an attribute called “name” which we wanted to restrict to
being between 1 and 20 chars long:
    <xs:attribute
name
=”nameuse=”required“>
        <xs:simpleType>
            <xs:restriction
base
=”xs:string“>
                <xs:maxLength
value
=”20” />

                <xs:minLength
value
=”1” />

            </xs:restriction>
        </xs:simpleType>
    </xs:attribute>

When this schema gets “converted” into a class representation, we get the following
for the attribute
    /// <remarks/>

    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string name {
        get {
            return
this
.nameField;
        }
        set {
            this.nameField
= value;
        }
    }

And then when this class gets converted back to XSD again, we get this:
    <xs:attribute
name
=”nametype=”xs:string
/>

All the restrictions have disappeared!
That’s because they’re not supported by the xsd.exe tool (or, more specifically,
by the internal classes used by xsd.exe to do the heavy lifting).

So that’s the problem with using the default SchemaValidator code: because
it validates incoming messages against the schemas in the WSDL, then unless you have
manually created your WSDL, the schemas in your WSDL will not be same as the original
schemas you created.

The solution is simple: validate the incoming messages against the original schemas.
And it just so happens that when you use the Web Services Publishing Wizard all
the original schemas are copied into the web service folder, in a sub folder called xsd.
What’s more, the original SchemaValidator code supports looking for schemas
in a folder called xsd – however, it simply has these take a backseat to the
ones in the WSDL.
Note: another solution is to handcraft your WSDL and put the original schemas
in there yourself – in fact, this is what everyone should do IMHO.

So the modifications I made to the original code are simple:

  1. Change the order for searching for schemas to being the xsd folder first, then
    the WSDL
  2. Added a schema-namespace check – if the schema has already been loaded, then we don’t
    attempt to load it again

All the changes I made are in a single class: ValidationExtension.cs

How to use

Compile the solution, and then either copy the SchemaValidation.dll assembly into
your web service’s bin folder, or give the assembly a strong name and put it in the
GAC.

Then, update the web.config file for your web service as per the example given in
the solution:

    <soapExtensionReflectorTypes>
        <!– add type=”$SoapExtensionReflectorType.FullName$,
$AssemblyName.Name$” /–>


        <add type=”SchemaValidation.ValidationExtensionReflector,
SchemaValidation, Version=1.0.0.0
“/>
    </soapExtensionReflectorTypes>
    <serviceDescriptionFormatExtensionTypes>
        <add type=”SchemaValidation.ValidationFormatExtension,
SchemaValidation, Version=1.0.0.0
“/>
    </serviceDescriptionFormatExtensionTypes>

Finally, open the code-behind file for your web service and do the following:

•    add a using SchemaValidation; statement
at the top
•    Add a [Validation] attribute
to all web methods where incoming messages should be validated

e.g.
    [WebMethod]
    [Validation]
    public string Test(MyType bp)
{

Now if you call the web method with a message which doesn’t validate against the schema,
you’ll get a SOAP Fault similar to this:


<?xml version=”1.0″ encoding=”utf-16″?>
<soap:Envelope
>
  <soap:Body>
    <soap:Fault>
      <faultcode>soap:Client</faultcode>
      <faultstring>System.Web.Services.Protocols.SoapException:
Incoming message failed validation: Error: The ‘name’ attribute is invalid – The value
” is invalid according to its datatype ‘String’ – The actual length is not equal
to the specified length.
   at SchemaValidation.ValidationExtension.xrs_ValidationEventHandler(Object
sender, ValidationEventArgs e)

The code for this solution can be downloaded from here:

SchemaValidation.zip
(21.99 KB)

If you have any issues with it, let me know.

Back to Tech Blog