All Posts

Implementing API Errors with Web API and FluentValidation

In a previous article, we talked about how APIs should return RESTful error responses so that API clients can act on them. There are plenty of articles like this one that talk about how to actually integrate the FluentValidation framework into the Web API pipeline, so we won’t go into the plumbing details. Below is a simple implementation of the RESTful API response model using these tools.

Validation Options

There are numerous options one has when choosing how to validate input using Web API. A classic option commonly used in Web API tutorials is using attributes for model validation. We started off going this route, but ran into a couple of issues:

  • Some of our validation rules depended on two adjacent properties. We found ourselves constantly overriding the default attribute validation behavior to account for this.
  • In many cases, our input models had shared nested models, but we needed different validation for the nested properties. Adding an attribute to the shared nested model did not allow it to be reused with different validation rules.

FluentValidation is a very flexible validation framework and is perfect for our needs.

WithState

FluentValidation provides an extension method when building validation rules called WithState. This method allows you to add any context you wish to the current rule. Whatever object you add to this context will be available to you when the rule fails. Let’s see it in action.

First, define an object that will hold the data we will need when validation fails:
[cc lang=”csharp”]
public class ErrorState
{
public ErrorCode ErrorCode { get; set; }
public string DocumentationPath { get; set; }
public string DeveloperMessageTemplate { get; set; }
public string UserMessage { get; set; }
}

public enum ErrorCode
{
None = 0,
Required = 10271992,
TooShort = 11051992
}
[/cc]

Next, define the validator that takes advantage of the WithState method and uses the aforementioned ErrorState object to encapsulate the type of validation failure:
[cc lang=”csharp” escaped=”true”]
public class UserInputModelValidator : AbstractValidator<UserInputModel>
{
public UserInputModelValidator()
{
RuleFor(x => x.Username)
.Must(x => x.Length >= 4)
.When(x => x.Username != null)
.WithState(x => new ErrorState
{
ErrorCode = ErrorCode.TooShort,
DeveloperMessageTemplate = “{0} must be at least 4 characters”,
DocumentationPath = “/Usernames”,
UserMessage = “Please enter a username with at least 4 characters”
});

RuleFor(x => x.Address.ZipCode)
.Must(x => x != null)
.When(x => x.Address != null)
.WithState(x => new ErrorState
{
ErrorCode = ErrorCode.Required,
DeveloperMessageTemplate = “{0} is required”,
DocumentationPath = “/Addresses”,
UserMessage = “Please enter a Zip Code”
});
}
}
[/cc]

Returning a RESTful Response

Before we can return the validation’s result to the client, we must first map it over to our RESTful API error structure.

The following objects are code representations of the JSON that we will send back to the client:
[cc lang=”csharp”]
public class ErrorsModel
{
public IEnumerable Errors { get; set; }
}

public class ErrorModel
{
public ErrorCode ErrorCode { get; set; }
public string Field { get; set; }
public string DeveloperMessage { get; set; }
public string Documentation { get; set; }
public string UserMessage { get; set; }
}
[/cc]
Notice how ErrorModel is very similar to ErrorState. ErrorsModel is our contract to the outside world. No matter how our validation implementation changes, this class must stay the same. Conversely, ErrorState is free to change as you improve your validation layer. You can add convenience methods, new enums, etc. without worrying about changing the JSON response to the client.

Once we run our validator, it is time for Web API to handle converting the result to something a client can consume. Below is code that can be placed in an ActionFilter:
[cc lang=”csharp” escaped=”true”]
private void ThrowFormattedApiResponse(ValidationResult validationResult)
{
var errorsModel = new ErrorsModel();

var formattedErrors = validationResult.Errors.Select(x =>
{
var errorModel = new ErrorModel();
var errorState = x.CustomState as ErrorState;
if (errorState != null)
{
errorModel.ErrorCode = errorState.ErrorCode;
errorModel.Field = x.PropertyName;
errorModel.Documentation = “https://developer.example.com/docs” + errorState.DocumentationPath;
errorModel.DeveloperMessage = string.Format(errorState.DeveloperMessageTemplate, x.PropertyName);

// Can be replaced by translating a localization key instead
// of just mapping over a hardcoded message
errorModel.UserMessage = errorState.UserMessage;
}
return errorModel;
});
errorsModel.Errors = formattedErrors;

var responseMessage = new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent(JsonConvert.SerializeObject(errorsModel, Formatting.Indented))
};
throw new HttpResponseException(responseMessage);
}
[/cc]
Our filter is doing the following:

  • Mapping our ErrorState object inline to the ErrorModel contract that we will be serializing
  • Creating an HTTP response object with the serialized data, which is then sent to the client by wrapping the response in and throwing an HttpResponseException

Now the client is ready to handle the nicely-structured API response.

Conclusion

FluentValidation is a powerful validation framework whose WithState method is a great entry point for custom logic including generating RESTful error responses. If you would like to see a full implementation, you can clone this GitHub repo. Keep in mind that this is a rough example and will most likely need to be modified to meet your exact needs and current infrastructure.