Blog

You are here: Blog / Effective Options Validation in ASP.NET Core: Best Practices and Techniques


  • Effective Options Validation in ASP.NET Core: Best Practices and Techniques?

    In this blog, we will explore options validation for configuration settings in ASP.NET Core projects. Typically, these projects have an "appsettings.json" file, which is used to provide configuration settings during application startup. These settings often include SMTP settings, third-party API keys, and other environment-specific configurations

    To bind these settings to a POCO (Plain Old CLR Object) class, we can use the options pattern, which involves implementing the "IOptions" interface. Here, "TOptions" represents the generic type that corresponds to our POCO class. Through dependency injection, we can then provide "IOptions" to access these configurations.

    It is crucial to validate these settings to prevent them from causing issues later on. So, let's delve into the concept of Options Validation.

    For better understanding, we will create an ASP.NET Core Web API project. For this demonstration, I am using Visual Studio 2022 and .NET 6.0 version with OpenAPI support, which configures Swagger by default. Contact us to download the demo project.

    In this demo project, there are three "appsettings.{environment}.json" files included to simulate real-life scenarios.


    The SmtpSettings are present in all three files.


    In the "Program.cs" file, all of these settings are bound to the "SmtpSettings" class at line number 11 (refer to the image below):


    In one of the controllers, these options are injected, and there is an endpoint that simply returns the options value in the response.


    Additionally, we have the flexibility to change the environment and load specific appsettings accord-ing to the chosen environment.


    Let's explore the validation part of the setup.
    The options validation can be achieved through three approaches:
    • 1. using DataAnnotation
    • 2. using Delegates
    • 3. Implementing the IValidateOptions interface

    1. Validation using DataAnnotation


    We have incorporated DataAnnotation attributes to the "SmtpSettings" POCO class for validation purposes. To trigger the validation, we need to chain the "ValidationDataAnnotation()" method in the "Program.cs" file, as shown in the image below:


    Now, let's modify the "appsettings.Development.json" file and populate the SMTP settings with in-correct values.


    Next, run the project using the development environment and execute the endpoint.


    After making incorrect changes to the "appsettings.Development.json" file, running the project with the development environment, and executing the endpoint, it should generate a validation error similar to the image above.

    Now, let's update the settings with the correct values and execute the endpoint again.


    Upon correcting the settings, the endpoint should return the correct response.

    However, it's important to note that if you modify the "appsettings.json" file while the project is run-ning, it will not immediately affect the response. ASP.NET Core reads the settings at project build time, so you need to restart the Web API project to see the changes.

    To execute this validation during startup rather than when executing an endpoint, you can use the "ValidateOnStart()" method, as depicted in the image below:


    Upon changing the settings back to incorrect values and starting the project, it will throw an excep-tion before running the application.


    2. Validation using delegates


    For more complex validation, delegates can be used. By chaining the "Validate()" method after or be-fore "ValidateDataAnnotations()", as needed, we can customize the validation process to suit specific requirements.


    We can create complex validation rules, such as "in the development environment, the email server should be with IP address 127.0.0.1."

    This can be achieved by writing the validation logic as shown in the image below (lines 14 to 22).


    Now if we run the development environment with below values then application will throw error before running.



    We can chain the Validate() method for different rules like below image


    3. Validation using IValidateOptions

    The IValidateOptions interface allows us to perform complex validations more efficiently and cleanly compared to using delegates. By creating a separate class that inherits from IValidateOp-tions, we can encapsulate the validation logic and ensure a modular and organized ap-proach to validating options.

    As shown in the image above, the SmtpServerValidation class inherits the IValidationOptions inter-face, which requires the implementation of the Validate method.

    In the image below, we have segregated the logic for validating the SMTP server for each environ-ment into separate classes. This approach allows us to organize and encapsulate the validation logic for each environment's SMTP server in a modular manner.


    Let's register the SmtpServerValidation class as a singleton and remove any previous validation dele-gates.


    Please update the smtp server value to 127.0.0.2 in the appsettings.Development.json file as shown in the image below.


    Ensure that the smtp server value is set to 127.0.0.1 for the development server. If the application is run with any other value, it will throw an error as expected.


    Conclusion

    In this article, we will delve into various methods for validating options. The first approach involves using DataAnnotation, the second utilizes delegates, and the third leverages the IValidateOptions in-terface. Additionally, we have developed a sample web API project that showcases these techniques.

    Depending on the project's complexity and specific requirements, one or more of these validation techniques can prove to be useful.