ASP.NET Core

ASP.NET Core is a framework for building web applications and HTTP services on .NET. Build around a clear request contract: how a route binds input, which service performs the work, what validation rejects, and what response the caller receives.

Keep that contract visible as the application grows. Dependency injection, middleware, authentication, persistence, and hosting provide useful structure, but their presence in a project does not prove the resulting service behaves correctly.

TL;DR

Quick Example

This complete two-file teaching service targets a .NET 10 SDK. It calculates a fictional quote without storage or authentication and binds only to loopback. No .NET SDK is available in this environment, so compilation and HTTP execution remain pending; source review is not a successful build.

Save Quote.csproj:

Save Program.cs beside it:

Proposed verification after dotnet run --project Quote.csproj: POST JSON {"quantity":2,"unitCents":125} to /quote and expect 200 with totalCents 250. Quantity 0 should produce 400. Also test malformed JSON, a noninteger quantity, missing/null body, and an unsupported method against the actual framework. These are acceptance expectations, not observed HTTP results. Do not expose this anonymous teaching endpoint as a protected production service.

Core Concepts

Routing and binding

Minimal API route handlers connect HTTP methods and paths to functions. The framework can bind values from routes, queries, JSON bodies, and registered services. Choose and document the intended source rather than accidentally accepting a different shape. Minimal API reference

Dependency lifetime

ASP.NET Core’s service container supplies registered dependencies. Singleton state is shared; request-scoped services belong to the request’s scope; transient instances have a different lifetime. A stateless arithmetic service is a reasonable singleton illustration, while a request-specific database context should not be captured by a singleton. Review actual dependencies and disposal. Dependency injection

Responses and validation

Binding a record does not establish every business invariant. The example explicitly checks numeric ranges before calculation. Results helpers return IResult; typed alternatives can preserve more endpoint metadata. Decide what the API documents and test the response contract. Minimal API responses

Build an acceptance matrix around the request

Use a small set of checks that identify which boundary failed. Pure business rules can have detailed unit cases; framework binding and middleware need integration tests; a deployed journey needs the published service.

Keep error handling at a clear boundary. Return useful public errors while retaining restricted diagnostics and a correlation reference. Do not serialize arbitrary exception messages or request bodies into a public response. Check cancellation and timeout propagation for downstream work; a disconnected client should not accidentally create unlimited ongoing operations.

If the service writes data, define transaction ownership and repeat behavior before adding retries. A route can time out after a database commit. Test lost-response and repeated-request behavior using the actual persistence/idempotency mechanism rather than assuming HTTP failure means no effect.

Add identity and deploy deliberately

Authentication establishes identity through a configured handler/scheme. Configuring it does not automatically restrict every endpoint. Apply the intended authorization policy and resource-level decisions, then verify allowed and denied cases. The anonymous quote example intentionally contains none of this wiring. Authentication overview

Choose a hosting model and verify the published output with the target runtime. ASP.NET Core can run with Kestrel directly or behind a suitable reverse proxy; proxy/load-balancer scenarios may require explicit handling of the original scheme and client information. Trust only the intended proxy path and review the corresponding settings. Hosting and deployment

Separate environment settings and secrets from source code. Confirm effective values in the target environment without printing credentials. Test readiness, dependency failure, graceful shutdown, restart, and a representative user request after publishing. A build succeeding on a developer machine is evidence about that build, not the deployed operating boundary.

Keep schema migrations separate from blindly starting every instance. Define which authorized process applies a migration, how compatibility works during overlap, and what recovery requires after writes. The web framework does not choose those data decisions for you.

Best Practices

Keep handlers focused

Use the route for transport concerns and delegate coherent business behavior to services. Avoid moving the same large handler into a service without clarifying its responsibility.

Verify lifetime and concurrency assumptions

Review shared mutable state, request-specific resources, async work, and disposal. Test the real dependency graph; a mock cannot expose every scope error.

Test negative access explicitly

Add denied identities, wrong owners, and missing-policy cases beside the successful path. A valid login is not evidence that object access is constrained correctly.

Publish and inspect the intended artifact

Record SDK/runtime, dependencies, settings requirements, and acceptance results. Compile and execute the example with the target toolchain before relying on it beyond source review.

Common Mistakes

Bad: Assuming successful JSON binding means valid business input.

Correct: Check domain constraints explicitly and test malformed as well as out-of-range input.

Bad: Capturing a request-scoped dependency in a singleton.

Correct: Use the appropriate lifetime and verify the container’s scope behavior.

Bad: Adding authentication and assuming every route is protected.

Correct: Apply and test the intended endpoint and resource authorization policies.

FAQ

Are Minimal APIs only for prototypes?

Choose the programming model from application needs, conventions, and maintainability. The important boundaries still include validation, access, persistence, testing, and operation.

Was the sample compiled here?

No. The project structure and source were reviewed, but no .NET SDK is available. Compilation and the proposed request matrix remain required.

Why is Pricing a singleton?

It has no mutable state or request-scoped dependencies in this example. Reevaluate that lifetime when its responsibilities change.

Related Topics

References