ASP.NET Core Fundamentals
itWeb development
ASP.NET Core Fundamentals
ASP.NET Core is the web framework in .NET. You use it to build HTTP APIs, server-rendered web applications, real-time services, and backends for other clients.
The framework gives you a web server, a request pipeline, routing, dependency injection, configuration, logging, security integration, and several endpoint models. You choose which parts your application needs.
ASP.NET Core is open source and cross-platform. It runs on Windows, Linux, and macOS. It works well when your team uses C# and .NET, needs one framework for APIs and web interfaces, or wants explicit control over the HTTP pipeline.
It may be a poor fit when your team has no .NET expertise, a static site is enough, or another platform already owns the application's operational model. ASP.NET Core supplies web infrastructure. It does not choose your domain model, database design, deployment topology, or security policy.
Start with the host
Every ASP.NET Core application starts by building a host. The host owns the resources needed to run the application:
- The HTTP server accepts connections.
- The middleware pipeline processes requests and responses.
- The service container creates registered dependencies.
- Configuration provides settings.
- Logging providers receive log events.
- Application lifetime signals coordinate startup and shutdown.
Current application templates use WebApplicationBuilder and WebApplication for this setup.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
var app = builder.Build();
app.UseExceptionHandler();
app.MapGet("/", () => "Hello from ASP.NET Core");
app.Run();
Read this file in two phases. Calls on builder.Services register capabilities before the host is built. Calls on app assemble the request pipeline and map endpoints after the build.
Kestrel is the default cross-platform web server. It can serve Internet traffic directly. Many production deployments place it behind IIS, Nginx, Apache, a load balancer, or an ingress controller. The proxy and application must agree about the original client address, scheme, host, and request limits.
Follow one request through the pipeline
The most useful mental model is a sequence of middleware around an endpoint.
Kestrel turns an incoming request into an HttpContext. The context carries the request, response, user, connection data, selected endpoint, and per-request services.
Each middleware component can do three things:
- Inspect or change the request.
- Call the next component.
- Inspect or change the response on the way back.
A middleware can also stop the sequence. This is called short-circuiting. Static-file middleware can return a file without reaching an application endpoint. Authentication can identify a user, while authorization can reject a request before endpoint code runs.
Order changes behavior. An exception handler must wrap components whose failures it catches. Authentication must run before authorization. Middleware that depends on a selected endpoint belongs after routing has selected one.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler();
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
Do not treat middleware order as formatting. It is application control flow.
Map URLs to endpoints
Routing matches an HTTP request to an endpoint. An endpoint combines executable code with metadata. Route patterns can include literal segments, parameters, constraints, and catch-all values.
app.MapGet("/orders/{id:int}", (int id) =>
Results.Ok(new { id }));
The route requires an integer id. Parameter binding converts the route value and supplies it to the handler. Endpoint metadata can describe authorization, OpenAPI behavior, rate limiting, and other policies.
Keep routes stable and resource-oriented. A route is part of the application's public contract. Changing it can break clients, bookmarks, monitoring, and caches.
ASP.NET Core offers several endpoint models:
| Model | Best starting point | Main shape |
|---|---|---|
| Minimal APIs | Focused HTTP APIs and small services | Route handlers mapped in code |
| API controllers | Larger APIs using controller conventions and filters | Classes derived from ControllerBase |
| MVC with views | Server-rendered applications organized by controllers | Controllers return Razor views |
| Razor Pages | Page-focused server-rendered applications | One page model per Razor page |
| SignalR | Real-time server-to-client communication | Hubs and persistent connections |
| Blazor | Interactive web user interfaces with .NET | Razor components and render modes |
These models share hosting, middleware, routing, dependency injection, configuration, logging, and security services. You can combine models when the boundaries stay clear.
Choose an API style deliberately
ASP.NET Core supports Minimal APIs and controller-based APIs. Microsoft recommends Minimal APIs for new HTTP API projects.
A Minimal API puts the route, handler, binding, and result close together:
app.MapPost("/orders", async (
CreateOrder request,
IOrderService orders,
CancellationToken cancellationToken) =>
{
var order = await orders.CreateAsync(request, cancellationToken);
return Results.Created($"/orders/{order.Id}", order);
});
Controllers provide a class-based model with attributes, filters, model state, and established MVC conventions. They can fit large APIs whose teams value that structure.
Neither style decides your architecture. A route handler or controller should coordinate HTTP concerns. Move domain decisions and infrastructure access behind explicit services. This keeps HTTP details from spreading through the application.
Let dependency injection manage object graphs
ASP.NET Core includes a service container. You register services during startup and request them where they are needed.
builder.Services.AddScoped<IOrderService, OrderService>();
The container builds the dependency graph and disposes services it owns. Three lifetimes cover most registrations:
| Lifetime | Instance boundary | Typical use |
|---|---|---|
| Transient | New instance for each resolution | Lightweight stateless work |
| Scoped | One instance per HTTP request | Request units of work and database contexts |
| Singleton | One instance for the application lifetime | Thread-safe shared services |
Lifetime errors are architectural bugs. A singleton must not capture a scoped service because the scoped object would outlive its request. A singleton also handles concurrent calls, so its mutable state requires careful synchronization.
Prefer constructor or handler-parameter injection. Avoid resolving arbitrary services through IServiceProvider inside business code. Explicit dependencies make ownership and testing visible.
Treat configuration as ordered input
ASP.NET Core reads configuration from providers. Default providers include JSON files, environment variables, and command-line arguments. Later providers override earlier providers for the same key.
Use the options pattern for related settings. Bind a configuration section to a typed class, validate it, and inject the appropriate options interface.
builder.Services
.AddOptions<PaymentsOptions>()
.BindConfiguration("Payments")
.ValidateDataAnnotations()
.ValidateOnStart();
Configuration is not secret storage. Secret Manager supports development secrets. Production deployments should use a controlled secret store supplied by the hosting environment.
An environment names an execution context such as Development, Staging, or Production. Use it to select diagnostics and hosting behavior. Do not use environment checks as authorization rules.
Log events you can operate
ASP.NET Core exposes logging through ILogger<T>. Providers send events to destinations such as the console or an observability platform. Categories and levels let operators filter events.
logger.LogInformation(
"Created order {OrderId} for account {AccountId}",
order.Id,
account.Id);
Use message templates with named fields. This preserves structured values for search and aggregation. Do not log passwords, tokens, connection strings, or unnecessary personal data.
Logs explain events. Metrics summarize behavior over time. Traces connect work across components. Production diagnosis usually needs all three, plus health checks that match the deployment platform.
Bind and validate untrusted input
ASP.NET Core can bind route values, query strings, headers, form fields, and request bodies to handler parameters or model properties. Binding answers, "Can this request data become this .NET value?"
Validation answers, "Does this value meet declared rules?" Domain logic must still answer, "Is this operation allowed and valid now?"
Keep those checks separate:
- Binding failures describe malformed input.
- Model validation describes invalid input shape or declared constraints.
- Domain validation enforces business invariants.
- Authorization decides whether the caller may perform the operation.
Never assume a C# type makes client input trustworthy. Apply size limits, validate fields, authorize resource access, and parameterize database operations.
Separate authentication from authorization
Authentication establishes an identity from a credential. An authentication scheme handles credentials such as cookies, bearer tokens, or external identity responses.
Authorization decides whether the current identity may access a resource or operation. ASP.NET Core supports roles, claims, policies, requirements, and handlers.
app.MapDelete("/orders/{id:int}", DeleteOrder)
.RequireAuthorization("CanDeleteOrders");
The policy belongs on the endpoint or a broader endpoint group. Resource-level authorization may also need application data. A valid login does not imply permission to access every record.
HTTPS protects data in transit. HSTS tells compatible browsers to prefer HTTPS on future requests. Data Protection protects framework-managed data such as authentication cookies. These controls solve different problems and do not replace authorization or secure secret handling.
Test through useful boundaries
Unit tests can exercise domain services without hosting the web application. Integration tests can run the ASP.NET Core application with WebApplicationFactory and TestServer. They send HTTP requests through routing, middleware, binding, and endpoint execution without opening a real network socket.
Use integration tests for behavior that depends on the framework boundary:
- Route and status-code contracts
- Authentication and authorization policies
- Serialization and validation
- Middleware ordering
- Service registration and configuration
- Error responses
Replace infrastructure only where the test boundary requires it. A test that replaces every important component proves little about the deployed application.
Publish an application, then operate it
dotnet publish creates deployable output. You can deploy framework-dependent output with a compatible .NET runtime or publish a self-contained application with its runtime included.
Deployment is more than copying files. Confirm these boundaries:
- The process receives correct configuration and secrets.
- The proxy forwards trusted scheme, host, and client information.
- Data Protection keys persist and are shared when required.
- Health probes represent liveness and readiness correctly.
- Logs, metrics, and traces reach the monitoring system.
- Graceful shutdown allows in-flight work to finish.
- Database changes use a controlled release process.
Do not expose development exception pages in production. They can reveal sensitive request and application details.
A practical learning path
- Build a Minimal API with several routes and typed results.
- Trace requests through custom and built-in middleware.
- Move application work into scoped services.
- Bind typed configuration and validate it during startup.
- Add structured logs and a health endpoint.
- Add authentication, then express authorization as policies.
- Write integration tests with
WebApplicationFactory. - Publish the application and test it behind the intended proxy.
- Compare Minimal APIs, controllers, Razor Pages, MVC, Blazor, and SignalR against real requirements.
- Study performance, distributed systems, data access, and security as separate disciplines.
The durable mental model is a flow. The host owns resources. Kestrel creates an HTTP context. Middleware surrounds the request. Routing selects an endpoint. Binding creates inputs. Authorization protects the operation. The endpoint coordinates application work. The response travels back through the pipeline.
