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.

[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.

{
  "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

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

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

//...

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

app.MapControllers();

//...

Share this Post