Skip to content

Business Rule Validation

Validate PublicationYear

In addition to basic request validation, it is common to validate business rules against static values or values from database.

Let's validate that PublicationYear has a minimum value of 1900 and max value of current year.

csharp
[HttpPost]
public IActionResult Create(CreateBookRequest request)
{
    logger.LogInformation("BookController.Create - Request {@request}", request");
    
    if (request.PublicationYear < 1900 || request.PublicationYear > DateTime.UtcNow.Year)
    {
        throw new ValidationException("Publication Year must be between 1990 and 2025");
    }

    //...
}


Test the API, the response is not consumable by frontend or mobile to show a readable message.

alt text


To get a similar response as basic validation, we can introduce an error middleware.

json
{
    "title": "",
    "status": "",
    "errors": "",    
}

Add ErrorMiddleware

First, add Newtonsoft nuget package, it's used to serialise objects into JSON and vice versa. alt text

Next, in Dtos folder, add ErrorResponse.cs

csharp
public class ErrorResponse
{
    public string Title { get; set; }
    public int Status  { get; set; }
    public Dictionary<string, List<string>> Errors { get; set; }
    
    
    public ErrorResponse(int code, string message)
    {
        Title = message;
        Status = code;
        Errors = new Dictionary<string, List<string>>();
    }

}


In Utilities folder, add ErrorMiddleware.cs

csharp
public class ErrorMiddleware
{
    private readonly RequestDelegate _next;

    public ErrorMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        //exception = exception.Get();

        var statusCode = HttpStatusCode.InternalServerError;
        if (exception is ValidationException)
        {
            Log.Information(exception.Message);
            statusCode = HttpStatusCode.UnprocessableContent;
        }
        else if (exception is AuthenticationException)
        {
            Log.Error(exception.Message);
            statusCode = HttpStatusCode.Unauthorized;
        }
        else
        {
            Log.Fatal(exception.Message);
            Log.Fatal(exception.StackTrace);
        }

        var camelCaseSerializerSettings = new JsonSerializerSettings
        {
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };

        var errorResponse = new ErrorResponse((int)statusCode, exception.Message);
        var result = JsonConvert.SerializeObject(errorResponse, camelCaseSerializerSettings);

        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)400;
        return context.Response.WriteAsync(result);
    }
}


In Program.cs, add

csharp
//... 

app.UseMiddleware(typeof(ErrorMiddleware)); //add this

app.MapControllers();

//...

Released under the MIT License.