LearnThatStack Ace your next interview

ASP.NET Core

ASP.NET Core 10 on .NET 10 LTS. Older ways are marked. Checked Nov 2026 121 code snippets

The model

  • Program.cs has two halves. Register services on builder.Services, call builder.Build(), then add middleware to app.
  • Middleware runs in the order you add it, and the response passes back in reverse. A component that skips next short-circuits the request.
  • Routing matches a request to an endpoint, the code that handles it. That's a controller action, a Razor Page or a minimal API handler.
  • The DI container creates services for you. A lifetime sets how long each one lives. Singleton is per app, scoped per request, transient per injection.
  • Configuration merges several sources into one set of keys. The later source wins, so environment variables override JSON files.
Kestrelthe web serverMiddlewarein the order you addFiltersMVC and Razor PagesEndpointthe chosen handlerINHttpContextcontroller or pagerun the actionminimal APISTOPshort-circuit no nextOUTresultresponsereverse order
Each middleware can work on the way in and on the way out. MVC filters wrap only controllers and Razor Pages, so a minimal API handler skips them.

Program.cs & hosting

how an app starts and what serves it

Kestrel and the host

Name the default serverKestrel. It's cross-platform and supports HTTP/3.
Say where it runsWindows or Linux in production. macOS for development and testing.
Host an app the old wayIWebHost
Host an app todayIHostWebApplication

TipKestrel can run alone or behind IIS, Nginx or Apache. A proxy can add HTTPS certificates and load balancing.

TrapIWebHost and WebHostBuilder are obsolete in .NET 10. Using them gives a compiler warning.

Middleware & filters

what runs around each request

Use, Run and Map

Do work, then call nextapp.Use(...)
End the pipelineapp.Run(...)
Branch on a path prefixapp.Map(...)
Stop a request earlySkip next. That short-circuits the pipeline.

TipWebApplication implements IApplicationBuilder, which builds the pipeline.

TrapA Run handler ends the pipeline, so nothing added after it is called.

Put middleware in order

app.UseExceptionHandler();first, catches the restapp.UseHttpsRedirection();app.UseStaticFiles();files stop hereapp.UseRouting();app.UseCors();app.UseAuthentication();app.UseAuthorization();app.MapControllers();

TrapAuthorization placed before authentication sees every user as anonymous.

Write a middleware class

Take next, then handle the requestRequestDelegateInvokeAsync(ctx)
Add it to the pipelineapp.UseMiddleware<T>()
Activate it per requestIMiddleware

TrapConvention-based middleware is created once for the app. Add scoped services as InvokeAsync parameters.

Handle exceptions

Catch errors in productionUseExceptionHandler()
Return a standard error bodyAddProblemDetails()
Map your own exceptionsIExceptionHandler

TipCreateBuilder turns on the Developer Exception Page in Development only.

TrapOnce the response has started, you can't change the status code.

Dependency injection

who creates your objects and how long they live

Service lifetimes

Singletonone instanceScopedrequest 1request 2Transienttime

Two requests, each injecting the service three times. Singleton keeps one instance, scoped makes two and transient makes six.

Share one per appAddSingleton<I, T>()
Share one per requestAddScoped<I, T>()
Make one per injectionAddTransient<I, T>()
Get a serviceAsk for the interface in the constructor.
Use a scoped service from a singletonIServiceScopeFactory

TrapA singleton holding a scoped service is a captive dependency. Scope validation throws in Development.

Keyed services

Register under a keyAddKeyedSingleton<I, T>("big")
Ask for one by key[FromKeyedServices("big")]

TipWithout keys, you get the last registration.

TrapA key with no registration fails only at run time, with an InvalidOperationException.

Settings, secrets & logs

where config comes from and how code reads it

Read settings

Read a raw stringconfig["Api:Key"]
Read a typed valueGetValue<int>(key, 30)
Read a connection stringGetConnectionString(name)
Bind a section to a classConfigure<T>(section)

TrapGetSection never returns null, so a null check won't catch a missing section.

Logging

Get a loggerILogger<T>
Write a structured messageLogInformation("Item {Id}", id)

TipSet levels in the Logging section. The default is Information.

TrapDon't build the string yourself. Named placeholders let providers store the values as fields.

Routes & REST APIs

map URLs to code

Version an API

Add versioningAsp.Versioning.MvcAsp.Versioning.Http
Do it the deprecated wayMicrosoft.AspNetCore.Mvc.Versioning
Send it in the path or a header/api/v1/productsHeaderApiVersionReader

TrapBy default every client must send a version. Set AssumeDefaultVersionWhenUnspecified to keep old clients working.

Rate limiting

Register and add itAddRateLimiter(...)app.UseRateLimiter()
Attach a policyRequireRateLimiting(name)
Name the four limitersFixed window, sliding window, token bucket, concurrency.

TrapPartition by user or API key. Partitioning on an IP address can itself open a denial of service risk.

Views, Razor Pages & Blazor

render HTML

Razor and Tag Helpers

Write a value into HTML@Model.Name
Add a model or service@model@inject
Bind an input to a propertyasp-for="Name"

TrapRazor HTML-encodes every string it outputs. Html.Raw skips that, so user input can inject markup.

Forms and anti-forgery

Say what the token stopsCSRF, a forged request that uses the user's cookie.
Check the token in MVC[ValidateAntiForgeryToken]
Show a bad form againreturn View(model);
Redirect after a saveRedirectToAction(name)

TipThe final redirect is Post/Redirect/Get. It stops a refresh from posting twice.

TrapAPIs that use a bearer token in a header, not a cookie, aren't exposed to CSRF.

Login, roles & security

who the user is and what they may do

Authentication

Register a JWT schemeAddAuthentication(...).AddJwtBearer(...)
Pick a schemeCookies for server-rendered apps, JWT for APIs.

TipAnonymous users get a challenge, a login redirect for cookie-protected pages or a 401. Users without permission get a forbid, a 403 with JWT.

TrapConfiguring authentication restricts nothing. Authorization does that.

Authorization and policies

Require sign-in, or open one endpoint[Authorize][AllowAnonymous]
Narrow it to a role or policyRoles = "Admin"Policy = "AtLeast21"
Define a policy with custom logicAddPolicy(name, ...)AuthorizationHandler<T>
Decide from the dataIAuthorizationService
Keep new endpoints privateFallbackPolicy

TrapWith a fallback policy, static files placed after authorization need a sign-in too.

CORS and HTTPS

Register a named policyAddCors(...)WithOrigins(url)
Apply itapp.UseCors(name)[EnableCors]
Enforce HTTPSUseHttpsRedirection()UseHsts()
Stop XSS and SQL injectionRazor encodes output and EF Core helps with SQL.

TipAdd a Content Security Policy, and show detailed errors only in Development.

TrapCORS isn't a protection, because it relaxes the same-origin policy. Never combine AllowAnyOrigin with AllowCredentials.

EF Core, caching & sessions

save data, cache it and keep per-user state

EF Core migrations

Add a migrationdotnet ef migrations add Name
Apply the pending onesdotnet ef database update
Deploy with a bundle or SQLdotnet ef migrations bundledotnet ef migrations script

TrapA migration may drop a column you meant to rename. Review the code before you apply it.

Distributed cache and session

Use Redis in productionAddStackExchangeRedisCache()
Read and write the cacheIDistributedCache
Add stampede protectionHybridCache
Turn on session stateAddSession()app.UseSession()

TipThe session cookie holds only the ID. Data expires after 20 idle minutes by default.

TrapAddDistributedMemoryCache stays on one server, so a farm needs sticky sessions or a shared cache.

SignalR, gRPC & background

push to clients, call services, work outside a request

SignalR

Push content to clientsWrite a hub. Each side can call the other's methods.
Name the transportsWebSockets, Server-Sent Events, long polling.
Scale across serversAzure SignalR Service or a Redis backplane.

TipMessages use JSON by default, or MessagePack for smaller binary messages.

Asked in interviews

AskedWhat is SignalR?

Background tasks

Run a long loopBackgroundServiceAddHostedService<T>()
Stop on shutdownPass stoppingToken to every awaited call.

TrapAfter a crash, StopAsync may never run, so work held in memory is lost. Durable jobs need a persistent queue.

Deploy, health & what's new

get it running and know what changed

Deploy and health checks

Create a Web API and run itdotnet new webapidotnet run
Build the publish folderdotnet publish
Include the runtimedotnet publish --self-contained
Pick a hostIIS, Linux with Nginx, Docker or Azure App Service.
Add a health endpointAddHealthChecks()MapHealthChecks(path)
Split liveness and readinessTag the checks. Liveness decides a restart, readiness decides traffic.

TipA process manager restarts the app after a crash or a reboot.

TrapBehind a proxy, the app may not see the original scheme or client IP. Add Forwarded Headers middleware.

Found this useful? Pass it on.
Pro · $10/mo

This sheet stays free. With Pro, you can:

  • Every answer on this sheet. Read the full answer to all 67 questions it links, not just the 9 free ones.
  • My sheet. The cards you star and the ones you miss in Quiz me, from every sheet, on one page to quiz and print.
  • PDFs. Download this sheet and My sheet, laid out for A4, with every question a link to its answer.
  • The rest of Pro. Every answer in the question library, and 1,000 AI credits a month.