ASP.NET Core 10 APIs · Lesson 5

Configuration and options validation

Reject an invalid operational setting before serving requests.

External .NET 10 SDK 10.0.403; ASP.NET Core server on your own desktop. Kestrel does not run in the SharpRune browser console.

Read or download the complete SDK project, then follow its setup on your computer. The separate C# companion practice below runs in SharpRune’s .NET 9 console environment.

Configuration supplies data rather than a validity guarantee

ASP.NET configuration can combine file values, environment variables, command-line arguments and other providers. Later values can override earlier ones according to provider order. A valid checked-in file therefore does not prove a valid deployed configuration. The external project binds one named section into a typed options object and validates the application's inclusive range at startup. The portable companion isolates that range predicate over three known integers. It has no configuration providers and makes no claim to reproduce provider precedence, reload behavior or the server's startup lifecycle inside the embedded console.

Read the normal value and the invalid override

The supplied appsettings file sets Limits:Maximum to twenty-five. Binding creates an options value whose Maximum satisfies the one-through-one-hundred predicate. Startup succeeds, and /limits returns maximum twenty-five. In a separate process, an environment variable named Limits__Maximum with value zero overrides that setting. Startup validation rejects it with an options validation failure before the endpoint serves requests. The console prints invalid, valid and invalid for zero, twenty-five and one hundred one. Those outputs establish the predicate independently, while the two server runs establish that binding and startup validation actually apply it.

Validate the semantic object at its intended boundary

The options registration chains binding, a named validation rule and ValidateOnStart. This makes an invalid operational setting an application-start failure rather than a surprise during the first relevant request. The endpoint receives IOptions<Limits> and consumes the already defined options contract. It does not parse raw strings on every call. Conversion failures and semantic range failures are different kinds of invalid configuration, so useful diagnostics should identify the section and rule without dumping unrelated values. In particular, configuration validation is not permission to log every setting when a deployment contains secrets.

Do not infer dynamic reload from a startup check

The sample chooses a startup boundary and the IOptions interface; it does not demonstrate a dynamically updated per-request snapshot or change notifications. Those interfaces have different lifetimes and behavior. If an operational value must change without a restart, specify which consumer observes it, how replacement values are validated and what happens when a reload is invalid. Avoid mutating an options object casually and assuming all registered consumers now share one consistent transactional setting. This small project intentionally proves a simpler invariant: the configured maximum must be valid before this process begins serving its endpoint.

Practice: test all four range edges

Run separate fresh processes with Maximum equal to one, one hundred, zero and one hundred one. Specify which starts successfully and what successful /limits responses contain before executing the runs. Use the environment override so the checked-in fixture stays unchanged. Keep an invalid numeric string as an additional independent conversion case rather than mixing it with a numeric value outside the range. The hint is that the predicate describes an inclusive range, and startup success is a different observation from merely finding the desired text in a configuration file.

Practice: make a missing setting explicit

Remove the Limits section in a separate project copy and observe the typed object's default Maximum. Decide whether absence should be invalid, have an explicit documented fallback or be supplied by another required provider. Implement that decision intentionally and update independent startup expectations. Then restore the file and use the override again to prove the source precedence still works. Do not choose a fallback merely to silence validation: an operational setting needs a reason appropriate to the application. Keeping absence and malformed data separate makes deployment failures easier to diagnose without weakening the accepted range.

Review value ownership and deployment evidence together

A configuration review should name the section, its typed shape, semantic invariants, validation timing and consumer interface. The project makes those choices inspectable in a few registration lines, while the companion provides deterministic rule examples. Preserve real process-start checks when changing provider order or validation timing; a unit test of the predicate alone cannot prove that the application invoked it. Conversely, a single valid startup cannot exercise every range edge. This lesson uses a harmless maximum, no secret and no external configuration service, so its claims remain limited to the providers and lifecycle it actually runs.

  • Validate operational meaning after binding, and choose when failure should stop the application.
  • Test missing values, conversion failures and invalid semantic ranges independently.
External .NET 10 SDK 10.0.403; ASP.NET Core server on your own desktop. Kestrel does not run in the SharpRune browser console.

Configuration and options validation — real ASP.NET Core 10 project

Set up the SDK project

  1. Save all files into one new empty directory, preserving their relative paths. Install the official .NET 10 SDK if it is not already available.
  2. From that directory run: dotnet restore WebLab.csproj --source https://api.nuget.org/v3/index.json
  3. Start only on loopback: ASPNETCORE_ENVIRONMENT=Development dotnet run --project WebLab.csproj --no-launch-profile --urls http://127.0.0.1:5080
  4. Use another terminal for the requests below, then press Ctrl+C in this project’s terminal to stop its server.
  5. After the normal request, stop the server. On a POSIX shell restart with Limits__Maximum=0 ASPNETCORE_ENVIRONMENT=Development dotnet run --project WebLab.csproj --no-launch-profile --urls http://127.0.0.1:5080 and observe the validation failure.

Download complete SDK project ↗

Project files

WebLab.csprojDownload file ↗
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>

  </PropertyGroup>
</Project>
global.jsonDownload file ↗
{
  "sdk": {
    "version": "10.0.403",
    "rollForward": "latestPatch"
  }
}
Program.csDownload file ↗
using Microsoft.Extensions.Options;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOptions<Limits>().Bind(builder.Configuration.GetSection("Limits")).Validate(x => x.Maximum is >= 1 and <= 100, "Maximum must be 1 through 100").ValidateOnStart();
var app = builder.Build(); app.MapGet("/limits", (IOptions<Limits> limits) => new { maximum = limits.Value.Maximum }); app.Run();
public sealed class Limits { public int Maximum { get; set; } }
appsettings.jsonDownload file ↗
{"Limits":{"Maximum":25}}

Expected result

GET /limits returns maximum 25. Restarting with Limits__Maximum=0 fails startup options validation and does not serve the endpoint.

Use the listed SDK and dependencies for this project. Open C# practice runs the separate portable exercise.

C# companion practice

using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

foreach (int maximum in new[] { 0, 25, 101 }) Console.WriteLine(maximum is >= 1 and <= 100 ? "valid" : "invalid");
Open C# practice

C# practice output

invalid
valid
invalid
Try this
Bind one section and validate its operational range at startup; a configuration source can still override the file.

Further reading