Phidiax Tech Blog

Adventures in custom software and technology implementation.

Using the Microsoft Enterprise Library Validation Block in Azure Pre-Compiled Functions

For those using WCF services who need validation, you are likely familiar with the Microsoft Enterprise Library Validation Block (Microsoft Patterns and Practices) and the WCF behavior that makes service input validation pretty easy to setup (If not, there is a deep-dive on using the validation block here). Perhaps you want to make the jump into Azure Functions but want to keep your validation without having to re-code it in any of the function language flavors. If that's the case, you've come to the right place!

This post deals with how to setup a pre-compiled Azure Function with the validation block. For how to use with C# Script (csx) Azure Functions, please see this entry.


Setting Up the Azure Function

For those not familiar, I will provide the steps here in creating the Azure Function app using Visual Studio 2017 and the Azure Functions and Web Job Tools. 

To create a new Azure Function app, create a new project using the Azure Functions template:


Once the project has been created, right-click it and select Add->New Item... and select the Azure Function type and provide a name:


Select the proper trigger type, in our case, Http trigger:


Similar to the C# Script version, we now have a basic function that can pull from the posted body and query string to access the "name" field and return basic text based on that. You can see that there is code in the return clause which does some rudimentary "validation" of the name field. This is fine for a simple request like this, but for more complex data-points, coding a pile of validation here isn't ideal (or fun). That's where enterprise library comes in!

Let's add the NuGet package to the project "EnterpriseLibrary.Validation" from Microsoft:


Setting Up the Validation Configuration

Before we can validate, we need to setup a class that contains the properties we expect to validate. For a simple demonstration, I have setup the following test class:

class ClassTest
{
    public string _value { get; set; }
    public string _name { get; set; }
}


To add that class to the function, paste it below the closing curly bracket for the Run method. Now that we have the class, we can parse the request body as JSON to obtain an instance of this type. Let's replace the dynamic data assignment line with the following:

// Get request body
var data = await req.Content.ReadAsAsync<ClassTest>();

Now let's right-click and add an Application Configuration file named Validation.config and paste a valid enterprise library configuration block:


I have setup a validation block configuration that will make sure the _name field is not null and has one or more characters, and that the _value field can be parsed as a Decimal. Let's paste the following validation code into the config file, and set it to always copy to output directory so it gets published with the rest of our code to Azure. 

Note: you don't have to manually create this XML as there is a visual tool that accompanies Enterprise Library to do this. You can download it here by downloading "EnterpriseLibrary6-binaries.exe". Note that it will expect you to run the PowerShell script to download the main Enterprise Library binaries from NuGet. This will fail because it is packaged with an old version of NuGet. Before running the PowerShell script, update the NuGet executable by running NuGet update -self, then the PowerShell script will work as-is.

Also note, since we're custom loading this config to bypass the configuration subsystem, the configSections should be removed from the top of the file:

<configuration>
  <validation>
    <type name="BlogFunctionDemo.FunctionValidator+ClassTest"
      defaultRuleset="Validation Ruleset"       assemblyName="BlogFunctionDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <ruleset name="Validation Ruleset">
          <properties>
              <property name="_name">
                  <validator type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.NotNullValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                      name="Not Null Validator" />
                  <validator type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.StringLengthValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                      upperBound="1" lowerBound="1" lowerBoundType="Inclusive" upperBoundType="Ignore"
                      name="String Length Validator" />
              </property>
              <property name="_value">
                  <validator type="Microsoft.Practices.EnterpriseLibrary.Validation.Validators.TypeConversionValidator, Microsoft.Practices.EnterpriseLibrary.Validation, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                      targetType="System.Decimal, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
                      name="Type Conversion Validator" />
              </property>
          </properties>
      </ruleset>
    </type>
  </validation>
</configuration>


Using the Validation Against POSTed Data

Ok, so we have our new class and our validation configuration file setup. Now, we just need to read that configuration and use it to validate the object. The resulting ValidationResults object can be passed back directly on failure, or could be parsed and logged instead as desired.

To load the configuration file, we need to figure out our local path. Luckily, we already know from our prior blog entry that we start in D:\home\site\wwwroot (this can easily be found using the Console feature of any Azure Function).  With that information, we can write the code to load the configuration into a validator and use it on the object. Additionally, we can return the validation results when the input is invalid, and run to completion when valid.

NOTE: Unlike the script Azure Function, we're using an XmlReader to bypass the Configuration subsystem. Using the config subsystem with the file configuration source results in the need to override assembly resolving so the Enterprise Library is properly found, in addition to the issue that we can't access the Azure Function's web.config properly. Instead, the Dictionary Configuration Source in Enterprise Library allows us to easily build configuration in code from the loaded XML.

Following is the final full Azure Function class:

using System.Linq;
using System.Net;
using System.Net.Http;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using System;
using System.Reflection;

namespace BlogFunctionDemo
{
    public static class FunctionValidator
    {
        [FunctionName("FunctionValidator")]
        public static HttpResponseMessage Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
        {

            log.Info("C# HTTP trigger function processed a request.");

            // Get request body

            ClassTest data;

            try
            {
                var dataLoader = req.Content.ReadAsAsync<ClassTest>();
                dataLoader.Wait();
                data = dataLoader.Result;
                if (data == null) throw new HttpRequestException("NULL Input");
                log.Info("Parsing as class succeeded.");
            }
            catch (System.Exception e)
            {
                log.Error(e.Message);
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }

            //Create a file config loader, set that as our default Enterprise Library Validation config source

            ValidationResults test;

            try
            {
                log.Info("Opening Config file");
                Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.ValidationSettings vs = new Microsoft.Practices.EnterpriseLibrary.Validation.Configuration.ValidationSettings();

                using (var xr = System.Xml.XmlReader.Create(@"D:\home\site\wwwroot\Validation.config"))
                {
                    xr.MoveToContent();
                    vs.ReadXml(xr);
                }

                log.Info("Adding Config to dictionary and setting as factory config");
                var config = new DictionaryConfigurationSource();
                config.Add("validation", vs);
                ValidationFactory.SetDefaultConfigurationValidatorFactory(config);


                log.Info("Create and use validator");
                //Create a validator from the factory and use it on our object. Results are in "test"
                test = ValidationFactory.CreateValidator<ClassTest>().Validate(data);
            }
            catch (System.Exception e)
            {
                log.Error(e.Message);
                return new HttpResponseMessage(HttpStatusCode.BadRequest);
            }

            log.Info("Return");
            return !test.IsValid
                ? req.CreateResponse(HttpStatusCode.BadRequest, test)
                : req.CreateResponse(HttpStatusCode.OK, "Hello " + data._name);
        }

        public class ClassTest
        {
            public string _value { get; set; }
            public string _name { get; set; }
        }
    }
}


Testing the Final Function

Let's build and publish the final product. Right-click the project and select Publish. Let's Publish to a new Azure Function App:



Let's also create a new App Service Plan to select the Consumption Plan:

Click OK and then Create to create the resources in Azure. This will also setup a Publish Profile to allow easy publication to Azure. Click Publish to upload to Azure so we can test.


Now that we're published, let's run with what we know to be invalid request: an empty _name and empty _value fields:



We have a return of the validation errors serialized as JSON directly stating both fields as invalid.

Now let's try with a valid name but invalid value:



And we can run a fully valid value, and the normal response is returned:

Loading

Privacy Policy  |  Contact  |  Careers

2009-2017 Phidiax, LLC - All Rights Reserved