FluentValidation and PostSharp for RESTful WCF parameter validation – Part 3

 

Now we can take a look at how to take advantage of everything we have put in place. As mention in the first post, two thing can happen after a validation failure:

  • A HTTP response code and JSON payload returned to the client explaining the error.
  • The invalid parameter ignored and a default one used instead.

So our PostSharp aspect needs to handle both of these cases.

[DataContract]
public class ErrorMessage
{
    public ErrorMessage()
    {
        Errors = new List<string>();
    }

    [DataMember]
    public string ParameterName { get; set; }
    
    [DataMember]
    public ICollection<string> Errors { get; set; }
}

This is the object that is serialised back to the client in the event of a failure with no default value. Now we need to override the OnInvoke method in order to apply our runtime interception.

public override void OnInvoke(MethodInterceptionArgs args)
{
    var errorMessage = new List<ErrorMessage>();            
    Arguments arguments = args.Arguments;

    foreach (var parameterValidation in _validationRules)
    {
        foreach (var validatorType in parameterValidation.ValidationRules)
        {
            var result = ValidateArgument(validatorType, arguments[parameterValidation.Parameter.Position]);

            if (result != null && !parameterValidation.HasDefaultValue)
            {
                var message = new ErrorMessage
                                  {
                                      ParameterName = parameterValidation.Parameter.Name
                                  };
                foreach (var validationFailure in result)
                {
                    message.Errors.Add(validationFailure.ToString());
                }
                errorMessage.Add(message);
            }
            else if(result != null)
            {
                args.Arguments.SetArgument(parameterValidation.Parameter.Position, parameterValidation.DefaultValue());
            }
        }
    }

    if(errorMessage.Any())
    {
        throw new WebFaultException<List<ErrorMessage>>(errorMessage, HttpStatusCode.BadRequest);                
    }

    base.OnInvoke(args);
}

So we iterate through the validation rules, where each rule composes of a parameter, the default value if there is one and the rules which should apply to it. We check whether the actual value passes the rule, and if there are any failures raise a WebFaultException available in the .NET 4 BCL.

In the cause of a failure which does have a default value defined, we use PostSharp to override the actual value with the default one before returning to the intercepted method.

The ValidateArgument method looks like:

static IEnumerable<ValidationFailure> ValidateArgument(Type validatorType, object argument)
{
    IValidator validator = (IValidator) Activator.CreateInstance(validatorType, null);

    var result = validator.Validate(argument);
    if (!result.IsValid)
    {
        return result.Errors;
    }
    return null;

}

This calls upon FluentValidation to perform the checking and returns the result.

In the event of a failure with no default value, the client get a nice HTTP Status code indicating an error…

clip_image002

And a message detailing why…

clip_image002[5]

Next up will be a sample solution up on GitHub.

blog comments powered by Disqus