ASP.NET interviews should reveal whether a developer can explain how a web request becomes a secure, reliable response. Recruiters need to distinguish modern ASP.NET Core experience from work with ASP.NET on .NET Framework: both are valuable, but middleware, hosting, application state, and UI models differ. Ask candidates to name their stack before evaluating an implementation.
This guide to 100+ ASP.NET Interview Questions and Answers includes 120 questions across beginner, intermediate, and experienced levels. It covers the web application lifecycle, dependency injection, configuration, endpoints, server-rendered interfaces, security, data boundaries, testing, operations, and modernization. The emphasis is ASP.NET application behavior rather than a general C# quiz, an Entity Framework reference, or a controller-only MVC guide.
How can recruiters identify potential AI cheating, deepfakes, impersonation, and unauthorized assistance during ASP.NET interviews?
Ask a candidate to trace middleware order, explain a failed authorization decision, or distinguish a Web Forms postback from an ASP.NET Core request. A fluent explanation alone does not establish independent ability to diagnose the actual application.
Sherlock AI helps recruiters detect potential AI-assisted cheating, deepfakes, candidate impersonation, and unauthorized real-time assistance during ASP.NET interviews.
Combine interview-integrity checks with a small code review and follow-up questions about hosting, trust boundaries, and failure behavior. Review suspicious signals in context rather than treating any one signal as proof of misconduct.
ASP.NET Interview Questions
Choose questions that match the application and role. State the runtime, web framework, authentication model, and deployment environment so that legacy and modern answers are evaluated against the right assumptions.
ASP.NET Interview Questions for Beginners
Establish the framework vocabulary, request lifecycle, application models, and essential security and state concepts. Include legacy questions only where the role requires that stack.
What is ASP.NET, and how is ASP.NET Core different from classic ASP.NET?
Which ASP.NET version does a modern application target?
How do MVC, Razor Pages, Minimal APIs, and Blazor differ?
What happens when an ASP.NET Core application receives an HTTP request?
What are Kestrel and IIS responsible for?
What does Program.cs configure in an ASP.NET Core application?
How do service registration and middleware registration differ?
What is dependency injection, and why is it useful in web applications?
How do transient, scoped, and singleton service lifetimes differ?
Where does ASP.NET Core read application configuration?
What is the options pattern?
What is middleware, and why does its order matter?
How does routing select an endpoint?
How do HTTP methods and status codes describe an API contract?
When would you use controllers instead of Minimal APIs?
What is model binding?
How is model validation different from model binding?
What does the ApiController attribute change?
What is Razor syntax, and how does it render values safely?
How do Razor Pages handlers and page models work?
What do Tag Helpers add to Razor views?
How are Blazor components different from Razor views?
What information does HttpContext provide?
How are authentication and authorization different?
How do cookies and server-side session state differ?
Why is HTTPS required even when an application uses authentication?
What is cross-site request forgery, and how does ASP.NET Core address it?
What is cross-site scripting, and what does Razor encoding protect against?
How do development and production environments differ?
How should an application report unexpected errors?
How do you write useful application logs?
Why should web request handlers use asynchronous I/O?
How do request cancellation tokens help an ASP.NET Core application?
How are static files served?
What is the difference between ViewData, ViewBag, and a strongly typed view model?
How does TempData support a redirect workflow?
What is the Web Forms page lifecycle in classic ASP.NET?
What are postback and IsPostBack in Web Forms?
What is ViewState, and is it the same as session state?
How do Global.asax, HTTP modules, and web.config relate to ASP.NET Core?
ASP.NET Interview Questions for Intermediate
Explore implementation choices in pipelines, binding, validation, authentication, caching, data access boundaries, UI state, and automated testing.
How do you choose the order of middleware in ASP.NET Core?
How do Map, MapWhen, and UseWhen change the request pipeline?
How do you return consistent errors with exception handling and Problem Details?
How do you validate strongly typed configuration before serving requests?
How do IOptions, IOptionsSnapshot, and IOptionsMonitor differ?
What is a captive dependency, and how do you avoid one?
How can middleware safely use a scoped service?
How do route groups simplify a Minimal API?
When should you use an endpoint filter instead of middleware or an MVC filter?
How do binding sources and request-body reading affect endpoint design?
How does automatic request validation differ between controllers and .NET 10 Minimal APIs?
How do request DTOs prevent overposting?
How do you handle file uploads safely?
How do JSON settings and content negotiation differ between controllers and Minimal APIs?
How do Razor Pages handlers and Post-Redirect-Get work together?
How do you protect browser form submissions against CSRF?
What does CORS allow, and what does it not protect?
When do you choose cookie authentication instead of bearer authentication?
How do you express authorization with policies and claims?
How do you authorize access to an individual resource?
How should ASP.NET Core session be used in a scaled application?
How do memory caching and distributed caching differ?
How does output caching differ from response caching?
How do cache keys and invalidation prevent incorrect responses?
Why use IHttpClientFactory for outbound HTTP requests?
How do you add timeouts and retries without duplicating side effects?
How do you keep database work bounded to a request?
How do you handle concurrent updates through an HTTP API?
How do you avoid blocking and unnecessary parallelism in a request handler?
How do SignalR hubs differ from ordinary request-response endpoints?
How do you secure SignalR connections and groups?
How do Blazor Web App render modes change where code runs?
Why can Blazor component initialization run more than once?
Why is hiding UI with AuthorizeView insufficient for authorization?
How do you test ASP.NET Core endpoints with WebApplicationFactory?
What belongs in a controller unit test versus an integration test?
How do you replace infrastructure dependencies in an integration test?
How do you customize model binding without breaking validation?
How do cancellation and streaming affect response design?
How should background work be started from an HTTP request?
ASP.NET Interview Questions for Experienced
Use production scenarios to assess reliability, isolation, performance, diagnostics, deployment, and migration decisions.
How do you diagnose ThreadPool starvation in an ASP.NET Core application?
How would you safely stream a large response without exhausting server memory?
How should request-size limits and upload limits be coordinated?
How do you propagate request cancellation while enforcing a downstream deadline?
When can retries turn an ASP.NET operation into a correctness bug?
How should a hosted service create and dispose scoped dependencies?
When should background work leave the web process for a durable queue?
How do you enforce tenant isolation across an ASP.NET request?
How do application rate limits behave when an application scales to several instances?
How would you prevent cache stampedes and stale data across multiple instances?
What must be configured for Data Protection in a multi-instance deployment?
Why can users lose authentication or session state after a deployment?
How do you safely process forwarded headers behind a reverse proxy?
How do host validation and security headers protect an ASP.NET application?
How would you prevent server-side request forgery in a URL-fetching endpoint?
How should production secrets be delivered and rotated?
How do you design useful distributed traces without leaking sensitive information?
How do liveness and readiness checks differ in production?
How do you make shutdown and rolling deployment safe for active requests?
What makes a container image suitable for an ASP.NET production deployment?
How would you deploy a database schema change without breaking a rolling release?
How do you respond when authentication credentials or signing keys are compromised?
What changes when SignalR runs on several application instances?
How do interactive server-side Blazor circuits affect scaling and reliability?
How would you investigate a latency regression after a release?
When are object pooling and allocation optimizations worthwhile?
When is Native AOT appropriate for an ASP.NET Core service?
How do you preserve correctness when optimizing response compression and caching?
How do you migrate System.Web HTTP modules and handlers to ASP.NET Core?
How would you plan an incremental migration from ASP.NET Framework?
Why is migrating Web Forms different from retargeting an ASP.NET Core project?
Can an ASP.NET Framework application share authentication cookies with ASP.NET Core?
How do you migrate legacy configuration and HttpContext dependencies safely?
How do you test security boundaries that unit tests cannot establish?
How do you handle partial failures in a request that spans several systems?
How do you prevent long-running requests from overwhelming the application?
How should feature flags and configuration changes be controlled in production?
How do you design safe error responses after response streaming has begun?
What would you inspect when an application works locally but fails behind IIS?
How would you design a reliable ASP.NET service for bursty, business-critical traffic?
ASP.NET Interview Questions and Answers
These answers follow the question bank in the same order and begin with direct responses candidates can adapt. Unless explicitly labeled legacy, examples assume ASP.NET Core 10 on .NET 10 with the Web SDK and usual implicit usings. Excerpts identify additional setup or application types where needed; separate snippets are not one combined application.
ASP.NET Interview Questions and Answers for Beginners
These answers connect basic web concepts to ASP.NET behavior and clearly separate current ASP.NET Core APIs from classic ASP.NET features.
1. What is ASP.NET, and how is ASP.NET Core different from classic ASP.NET?
ASP.NET is Microsoft's family of technologies for building web applications and HTTP services with .NET. The name covers both classic ASP.NET on .NET Framework and the newer ASP.NET Core framework, so I clarify which stack a project uses before discussing APIs.
ASP.NET Core is cross-platform and has its own hosting, middleware, dependency-injection, and configuration systems. Current applications can run on modern .NET and use controllers, Razor Pages, Minimal APIs, or Blazor. Classic ASP.NET commonly uses System.Web on Windows with IIS; Web Forms and ASP.NET MVC 5 belong to that older family.
The distinction matters in code. Microsoft.AspNetCore.Http.HttpContext is not System.Web.HttpContext, and Core does not provide the Web Forms server-control lifecycle. Migrating a classic application therefore involves more than changing a target-framework setting. I identify the application's web framework, runtime, hosting model, and dependencies separately rather than assuming that every feature called ASP.NET works in both environments.
2. Which ASP.NET version does a modern application target?
For a new application in this guide, I use ASP.NET Core 10 on .NET 10, with the latest applicable servicing patches. .NET 10 is the current LTS baseline used here; the target framework is net10.0. ASP.NET Core follows the .NET support lifecycle rather than having an unrelated major-version support schedule.
A web project normally uses the Web SDK. This project-file excerpt establishes the baseline, but it does not pin an SDK patch or configure deployment:
XML example
For an existing application, I check its target framework, installed SDK, deployment runtime, package compatibility, and support status before upgrading. A .NET Framework 4.x application is not an ASP.NET Core 4 application. Likewise, preview SDK availability is not a reason to describe preview functionality as the supported production baseline.
3. How do MVC, Razor Pages, Minimal APIs, and Blazor differ?
MVC separates request handling into controllers, application data into models, and server-rendered output into views. Razor Pages organizes server-rendered applications around pages and their handlers. Both use Razor for HTML templates, but their request-handling organization differs.
Minimal APIs map HTTP endpoints directly with concise handler functions and are useful for HTTP services without the controller structure. They still use routing, dependency injection, authentication, and other ASP.NET Core infrastructure; minimal syntax does not mean security or validation can be omitted.
Blazor builds user interfaces from Razor components. Depending on the configured render mode, a component may render static HTML or provide interactivity through server-side execution or WebAssembly. A .razor component is not interchangeable with a .cshtml MVC view or Razor Page.
I choose based on the UI and API requirements, team conventions, and operational constraints. An application can combine compatible models, such as Razor Pages for a back-office interface and API endpoints for integrations. These are application models, not competing versions of the .NET runtime.
4. What happens when an ASP.NET Core application receives an HTTP request?
The server receives an HTTP request and exposes it through an HttpContext. ASP.NET Core then runs the configured middleware pipeline. Middleware can inspect or modify the request, invoke the next component, short-circuit with a response, and perform work while the response returns through the pipeline.
Routing matches an endpoint using request information such as the path and HTTP method. Other middleware may authenticate the caller and enforce authorization before the selected endpoint executes. The endpoint can bind input, run application logic, and produce a response containing a status code, headers, and possibly a body.
The exact sequence depends on configuration: a static-file handler or an error handler may finish a request before a controller runs. A request also does not imply a new operating-system thread reserved for its entire lifetime. Asynchronous I/O lets the application release threads while awaiting external work. I describe the pipeline and endpoint separately because many failures occur before business logic is reached.
5. What are Kestrel and IIS responsible for?
Kestrel is ASP.NET Core's cross-platform web server. It accepts connections and handles HTTP processing when the application uses it. IIS is a Windows web server that can host an ASP.NET Core application through the ASP.NET Core Module, handling integration with the IIS process and hosting infrastructure.
The distinction depends on the hosting model. With IIS out-of-process hosting, IIS forwards requests to the application's Kestrel process. With IIS in-process hosting, the application runs inside the IIS worker process and uses the IIS HTTP server implementation rather than Kestrel for that request path. Saying IIS always proxies to Kestrel is therefore inaccurate.
Kestrel can also serve applications without IIS, including Linux deployments. A reverse proxy may supply additional infrastructure functions, but it is not a mandatory component in every topology. I verify the actual deployment model before diagnosing ports, process restarts, forwarded headers, TLS termination, or request-size limits because those settings can exist at different layers.
6. What does Program.cs configure in an ASP.NET Core application?
Program.cs is the usual composition point for a modern ASP.NET Core application. It creates the builder, registers services, builds the application, configures request processing, maps endpoints, and starts the host. Top-level statements keep the entry point concise without removing those responsibilities.
This complete minimal example assumes an ASP.NET Core 10 Web SDK project:
C# example
CreateBuilder supplies defaults for configuration, logging, and hosting. Build creates the application using the registered services. MapGet defines an endpoint; it does not start the server. Run() starts the application and waits for shutdown.
Larger applications should keep this composition readable by moving cohesive registration or mapping logic into extension methods. Older Core projects may use Startup.ConfigureServices and Startup.Configure; that is a hosting-organization difference, not evidence that middleware or dependency injection disappeared in newer versions.
7. How do service registration and middleware registration differ?
Service registration makes dependencies available through the dependency-injection container. Middleware registration defines components that participate in HTTP request processing. These are related but separate steps, and many framework features require both.
For Razor Pages, the startup excerpt is:
C# example
AddRazorPages registers the framework services needed to support pages. MapRazorPages exposes their endpoints. Merely registering page services does not make their routes reachable. Likewise, adding a service to the container does not mean it runs once for every incoming request.
I normally complete service registration before builder.Build(). Then I configure the application's pipeline and endpoints on the built app. Extension methods beginning with Add, Use, and Map often indicate these roles, although names alone are not a formal guarantee. Understanding each method's responsibility makes missing setup and incorrect ordering easier to diagnose.
8. What is dependency injection, and why is it useful in web applications?
Dependency injection means a class receives the services it needs instead of constructing or globally locating them itself. ASP.NET Core has a built-in container that creates registered dependencies and supplies them to controllers, page models, endpoint parameters, and other supported consumers.
For example, a reporting controller can receive an IReportService through its constructor. The controller handles HTTP concerns while the service implements reporting behavior. Tests can supply a controlled implementation without creating a real database or sending an email. This is useful only if the interface represents a meaningful boundary; creating an interface for every class is not an automatic design improvement.
Registration also establishes lifetime and ownership. I choose how long a service lives, ensure its dependencies are compatible with that lifetime, and let the container dispose objects it creates when their lifetime ends. I avoid retrieving arbitrary services from a global provider because that hides dependencies and makes lifetime errors harder to see. DI manages construction; it does not itself make a service thread-safe.
9. How do transient, scoped, and singleton service lifetimes differ?
A transient service is created each time the container resolves it. A scoped service is shared within a scope; in ordinary HTTP processing, ASP.NET Core creates a scope for each request. A singleton is shared across the application service provider's lifetime.
A registration excerpt, assuming the listed application interfaces and implementations exist, is:
C# example
I use the lifetime that matches ownership and state. Request-specific work often fits scoped services, while a stateless, thread-safe shared utility may fit a singleton. A singleton must not directly capture a scoped service; that would keep a request-scoped dependency alive beyond its intended scope. Transient dependencies captured by singletons also effectively live with that singleton.
Scoped does not mean globally one instance per user, browser session, or thread. Background work needs an explicitly created scope when it resolves scoped services. Blazor interactive server circuits also have lifetime behavior that differs from ordinary request scopes.
10. Where does ASP.NET Core read application configuration?
ASP.NET Core reads configuration through providers combined into an IConfiguration. The default builder includes sources such as appsettings.json, the environment-specific JSON file, development user secrets when configured, environment variables, and command-line arguments. Later providers can override earlier values for the same key.
A simple configuration file might contain:
JSON example
The key is accessed as Catalog:PageSize. In environment variables, double underscores represent hierarchical separators, so Catalog__PageSize can override that setting across supported platforms.
I separate application settings from secrets. Source-controlled JSON is suitable for nonsecret defaults, not production passwords. User secrets help development but are not an encrypted production vault. Configuration values are also untrusted operational input: I validate required values and ranges rather than letting an invalid page size or missing destination fail much later in a request. Precedence can be customized, so I inspect the actual builder configuration when values appear unexpected.
11. What is the options pattern?
The options pattern groups related settings into a typed class instead of scattering configuration-key strings throughout application code. The application binds a configuration section to that class and injects an options interface into consumers.
This registration excerpt assumes a CatalogOptions class with a writable integer PageSize property:
C# example
The validation rejects an invalid configuration during application startup instead of allowing an unsuitable value to flow into requests. A consumer can receive IOptions<CatalogOptions> and read its Value property.
I choose the options interface deliberately. IOptions<T> is appropriate for settings that need not refresh through that interface; IOptionsSnapshot<T> provides scoped snapshots, while IOptionsMonitor<T> supports current values and change notifications when providers support reload. A typed class improves discoverability, but it does not guarantee valid settings unless validation is actually configured.
12. What is middleware, and why does its order matter?
Middleware is a component in the HTTP request pipeline. It can perform work before the next component, call that component, and perform work afterward. It can also stop processing by writing a response without calling the next delegate.
This excerpt adds a response header before allowing later components to run:
C# example
Order matters because a component can only observe work that passes through it. Error handling normally belongs early enough to catch exceptions from later components. Authentication must establish the caller before authorization checks rely on that identity. Middleware that uses endpoint metadata must run after routing has selected an endpoint.
The response unwinds in reverse order after await next, but modifying headers then may be too late if the response has already started. I place response-header changes before downstream execution or register an appropriate OnStarting callback. A middleware component is not an MVC action filter, and it can process requests that never reach MVC.
13. How does routing select an endpoint?
Routing matches an incoming request to an endpoint using route patterns and metadata such as HTTP-method constraints. The selected endpoint contains the handler and associated metadata, which later infrastructure can use for authorization or other policies.
This complete small application maps a typed route value:
C# example
The int constraint helps distinguish matching routes; it is not a substitute for business validation. A numeric identifier can still refer to a missing product or a resource the caller cannot access. The example echoes an identifier and does not implement a real lookup.
Route matching is not simply 'the first line always wins.' Specificity, constraints, endpoint metadata, and explicit ordering can affect selection; ambiguous matches should be corrected rather than hidden with assumptions. I also distinguish a missing path, commonly 404, from a matching path with an unsupported method, commonly 405.
14. How do HTTP methods and status codes describe an API contract?
HTTP methods describe the requested operation, while status codes describe the result. GET retrieves a representation and should not perform intentional state-changing work. POST commonly creates resources or invokes commands. PUT replaces or creates the state of the addressed resource according to the API contract, PATCH applies a defined partial modification, and DELETE requests removal.
I return status codes that clients can interpret reliably: 200 for a successful response with content, 201 for creation with an appropriate resource location, 204 for success without a body, 400 for invalid input, 401 when authentication is required or invalid, 403 for denied access, and 404 when the resource is not found or deliberately not disclosed.
Idempotency concerns the intended effect of repeated requests, not identical response bodies or codes. A repeated DELETE can return a different status while remaining idempotent. I do not treat all errors as 200 responses containing an error string, because that breaks monitoring and client handling. Validation and authorization remain server responsibilities regardless of the chosen verb.
15. When would you use controllers instead of Minimal APIs?
I use controllers when their organization and conventions fit the application: related actions, controller-level attributes, MVC filters, established model-binding behavior, or server-rendered views. I use Minimal APIs when direct route-handler mapping produces a clearer service with less ceremony. Neither model is inherently restricted to toy or enterprise applications.
Controllers require the corresponding services and endpoint mapping. For an API-only project, AddControllers and MapControllers provide the usual setup; AddControllersWithViews is appropriate when MVC views are required. Minimal API handlers are mapped directly with methods such as MapGet and MapPost.
The important differences are framework behavior and composition, not whether one can return JSON. Both can use dependency injection, authentication, authorization, and asynchronous operations. I avoid assuming that a controller attribute, MVC filter, or automatic validation rule also applies to a Minimal API handler. For example, controller [ApiController] behavior is distinct from the validation support configured for Minimal APIs in ASP.NET Core 10. I choose one consistent style unless mixing models has a concrete benefit.
16. What is model binding?
Model binding converts request data into action parameters or model properties. In MVC and Razor Pages, possible sources include route values, query strings, submitted form fields, and uploaded files; body content such as JSON is handled through input formatters when body binding is selected.
For a controller action, explicit source attributes can make a contract clearer:
C# example
This method belongs inside an ASP.NET Core controller derived from ControllerBase, with Microsoft.AspNetCore.Mvc imported and controllers registered and mapped. It demonstrates binding, not a production product lookup.
Binding answers whether data can be obtained and converted, not whether the requested operation is permitted. I use dedicated input models to avoid accepting writable fields that callers must not control. I also avoid multiple body-bound parameters for one action: the request body is normally read once, so related JSON fields belong in one request object. Minimal API binding has its own documented conventions.
17. How is model validation different from model binding?
Model binding obtains and converts incoming values. Model validation checks whether the resulting values satisfy declared rules, such as a required name or an allowed numeric range. A nonnumeric value supplied for an integer can cause a binding error, while a successfully parsed negative quantity can violate a validation rule.
In MVC and Razor Pages, binding and validation errors are recorded in model state. Server-rendered handlers commonly inspect ModelState.IsValid and redisplay the form with errors. API controllers using [ApiController] ordinarily return an automatic 400 response for invalid model state unless that behavior is deliberately customized.
Validation attributes express useful structural rules, but they do not replace authorization, database constraints, or all business rules. A well-formed account ID can still belong to someone else, and availability can change after validation. Client-side validation improves usability but can be bypassed. I always enforce necessary rules on the server, use focused request models, and distinguish input errors from unexpected application failures so clients receive actionable responses.
18. What does the ApiController attribute change?
[ApiController] enables conventions intended for HTTP API controllers. Important behaviors include requiring attribute routing, inferring parameter binding sources in supported cases, and automatically returning a 400 response when model state is invalid. The automatic invalid-model response normally uses a structured validation-problem payload.
A typical declaration is:
C# example
The application must separately register controllers and map them. The attribute does not turn an arbitrary class into a running application, add persistence, or protect the endpoint from unauthorized access.
I use explicit binding attributes when inference would make the contract unclear, and I configure automatic behavior only for a specific reason. [ApiController] is not [Authorize], and it does not guarantee that input is safe for every use. Its conventions apply to controller APIs, not automatically to Minimal API route handlers or Razor Pages.
19. What is Razor syntax, and how does it render values safely?
Razor is a templating syntax that combines HTML with C# using the @ transition. In MVC views and Razor Pages, the server evaluates the template and sends the resulting HTML to the browser. The browser does not execute the C# source from the template.
For a view whose model is a string, this example displays the value:
Razor view example
Ordinary string output is HTML-encoded, so a value containing angle brackets is displayed as text rather than interpreted as a new HTML element. That default is an important defense against injecting markup into ordinary HTML output.
I do not use Html.Raw for untrusted text because it bypasses that protection. HTML encoding also is not a universal solution for JavaScript, URL, or other output contexts. Data must be validated and encoded for its actual destination. Razor should mainly render prepared data; database access, authorization decisions, and substantial business logic belong in appropriate application layers rather than being hidden in templates.
20. How do Razor Pages handlers and page models work?
Razor Pages organizes a server-rendered application around a .cshtml page and, commonly, a companion PageModel class. The @page directive makes the Razor file a routable page. Handler methods such as OnGet and OnPost process requests for the relevant HTTP methods.
The page model prepares display data, accepts deliberately bound input, invokes application services, and chooses a result. For example, a POST handler can validate an input model, save through a service, and return RedirectToPage to avoid accidental resubmission on refresh. If validation fails, it can return Page() and redisplay the form with errors.
Page handlers are not Web Forms lifecycle events. There is no automatic server-control tree, postback event model, or ViewState carrying control state. Although both approaches can be described as page-oriented, their execution models are different. I keep substantial business logic outside the page model and remember that binding a property for POST does not automatically enable GET binding; supporting GET binding is an explicit choice for appropriate non-sensitive values.
21. What do Tag Helpers add to Razor views?
Tag Helpers let server-side code participate in generating HTML elements in Razor views and pages. They use HTML-like attributes rather than requiring every framework operation to appear as an explicit C# helper call. Built-in Tag Helpers support tasks such as generating links, forms, input names, and validation messages.
With the MVC Tag Helpers enabled through _ViewImports.cshtml, this link targets a Razor Page:
Razor view example
The rendered browser output contains an ordinary link with a generated destination, not a client-side asp-page framework operation. The destination page and route must exist for the link to represent a useful navigation path.
Tag Helpers do not make a page a Web Forms page and do not create a postback control lifecycle. They also do not authorize the destination. A hidden link or generated form remains presentation; the target endpoint must enforce its own access rules. I use them to keep markup understandable and routing-aware while leaving business rules and security checks on the server.
22. How are Blazor components different from Razor views?
A Blazor component is a reusable UI unit, typically defined in a .razor file, with parameters, rendering logic, and lifecycle behavior. A Razor view is a .cshtml template normally rendered in response to an MVC action. They share Razor syntax but belong to different programming models.
In a Blazor Web App, render modes determine whether a component is statically rendered or interactive and where interactive execution takes place. Interactive Server executes component logic on the server and communicates UI updates over a connection. Interactive WebAssembly executes interactive component code in the browser. Interactive Auto can use server interactivity initially and WebAssembly on later visits after the client bundle is available; it does not simply move an active component between runtimes mid-session.
I do not assume a component is interactive merely because it contains an event handler. The application must support the required render mode. Client-executed code is also not a trusted security boundary: authorization for protected data and operations must be enforced by the server even if the UI hides controls.
23. What information does HttpContext provide?
HttpContext represents the current HTTP request and response. It exposes the request path, headers, body, response, authenticated user, connection information, request services, cancellation signal, and per-request item storage. Middleware and endpoints use it to work with the current exchange.
HttpContext.Items is useful for sharing data among components during that request. It is not persistent session storage. Once the request finishes, application code must not retain the context and assume its members remain usable.
I also avoid accessing one context concurrently from unrelated tasks because HttpContext is not thread-safe. If background work needs a correlation ID or other value, I copy the required immutable data while the request is active and enqueue that data rather than the context itself. IHttpContextAccessor can expose the current context in suitable services, but it may be null outside HTTP processing and should not become a hidden global dependency. For code that only needs a user identifier, an explicit application-level input is often easier to test.
24. How are authentication and authorization different?
Authentication establishes who the caller is by validating credentials through a configured scheme, such as a protected authentication cookie or a bearer token. Authorization decides whether that caller is allowed to perform a particular operation or access a resource.
An authenticated user is not automatically authorized for every endpoint. For example, a signed-in employee may be allowed to view a catalogue but not change prices. An [Authorize] attribute or endpoint authorization requirement enforces configured access rules; policies can express roles, claims, or custom requirements. Resource ownership often requires a check against the specific resource, not merely a generic 'signed in' rule.
A challenge asks the authentication mechanism to handle an unauthenticated request, while a forbid result represents denied permission for an authenticated caller. The resulting HTTP behavior depends on the scheme and endpoint; web-page cookie flows and API responses can differ. I configure authentication schemes and authorization deliberately and test anonymous, permitted, and denied callers. Hiding a button in Razor or Blazor is not a substitute for server-side authorization.
25. How do cookies and server-side session state differ?
A cookie is data stored by the client and sent with matching HTTP requests. Session state stores application data on the server, with a session cookie normally identifying the corresponding session. An authentication cookie has a separate purpose: it represents authentication information protected and interpreted by the authentication system.
I use session only for transient convenience data, not as the sole durable record of an order or payment. Session can expire or disappear, and applications must continue safely when it does. A distributed deployment needs an appropriate shared session store or an explicitly understood alternative; per-process memory is not automatically shared among instances.
Cookies require suitable scope and security settings such as Secure, HttpOnly, and SameSite, chosen for their actual use. HttpOnly limits JavaScript access but does not prevent the browser from sending the cookie or eliminate CSRF. Session state also is not one immutable object per human: users can use multiple browsers, clear cookies, or make concurrent requests. I minimize stored data and keep authorization independent from a mutable session flag.
26. Why is HTTPS required even when an application uses authentication?
HTTPS protects data in transit and helps the client authenticate the server through TLS. Application authentication identifies a user; it does not encrypt the network connection. Without HTTPS, credentials, cookies, tokens, and sensitive responses can be exposed or modified in transit even when the login code itself is correct.
ASP.NET Core can redirect HTTP requests to HTTPS, and HSTS can instruct supporting browsers to use HTTPS for the host on future requests. These mechanisms have different roles. A redirect does not retroactively protect data already sent over HTTP, and HSTS deployment requires careful consideration of domain and subdomain behavior.
When TLS terminates at a reverse proxy, I verify trusted forwarded-header configuration so the application understands the original request scheme. Otherwise, HTTPS redirects and generated URLs may behave incorrectly. I also use secure cookie settings and avoid mixed-content resources. TLS is necessary but not sufficient: it does not fix broken authorization, unsafe HTML output, leaked secrets in logs, or compromised clients. Each protection addresses a different boundary.
27. What is cross-site request forgery, and how does ASP.NET Core address it?
Cross-site request forgery tricks a browser into sending an unwanted request to a site where the user is already authenticated. The browser may automatically attach authentication cookies, so the presence of a valid cookie does not prove that the user intended the operation.
ASP.NET Core provides antiforgery tokens that pair server-side validation with a token supplied through the legitimate page or client workflow. Razor Pages provide antiforgery protection by default for relevant handlers, and MVC form applications commonly use the form Tag Helper with server-side antiforgery validation. Generating a token without validating it is not protection.
I keep state-changing work out of GET handlers and apply protection to the actual authentication and request model. An API using a bearer token explicitly attached in an authorization header has a different CSRF exposure from cookie-authenticated endpoints, but calling something an API does not remove risk if cookies are still accepted. SameSite cookies provide useful defense in depth, not a universal replacement for antiforgery design. CORS controls selected cross-origin browser access; it is not an authorization or general CSRF defense.
28. What is cross-site scripting, and what does Razor encoding protect against?
Cross-site scripting occurs when untrusted content is interpreted as executable content in another user's browser. The content might come from a form, stored database value, URL, or external integration. Storing it first does not make it trusted.
Razor's ordinary string output is HTML-encoded, which prevents a string rendered into an HTML text position from becoming markup. That is why displaying a user-supplied name with @Model.Name is generally safer than passing the name to Html.Raw. However, an HTML-safe string is not automatically safe inside JavaScript source, a CSS value, or every URL attribute.
I keep untrusted text out of executable contexts, use context-appropriate APIs, validate allowed URL schemes, and sanitize with a suitable maintained policy when rich HTML is a real requirement. Content Security Policy can reduce the impact of some injection paths but does not make unsafe output correct. Input validation alone is insufficient because the same value can be safe in one output context and dangerous in another. Server-side authorization remains necessary even when output encoding is correct.
29. How do development and production environments differ?
The environment identifies the application's operating context, commonly Development, Staging, or Production. Code and configuration can use it to choose diagnostic behavior or load appropriate settings. It is not a substitute for a separate security boundary or a deployment approval process.
For example, this pipeline excerpt avoids exposing development diagnostics outside Development:
C# example
The application must provide a suitable /Error endpoint. HSTS settings also need to fit the deployment's HTTPS and domain policy.
I do not enable the developer exception page in a publicly accessible environment because it can reveal implementation details. I verify the actual environment selected by the host rather than assuming the build configuration determines it: Release compilation and the Production environment are different concepts. Environment-specific configuration can override ordinary settings, but secrets still require appropriate storage. A staging environment should resemble production behavior closely enough to reveal configuration and deployment defects before a release.
30. How should an application report unexpected errors?
An application should log unexpected exceptions with useful diagnostic context while returning a safe response to the caller. Production clients should not receive stack traces, connection strings, SQL details, or internal file paths. Expected failures, such as a missing resource or invalid input, should use deliberate responses rather than being treated as unexpected crashes.
ASP.NET Core exception-handling middleware can catch exceptions from later pipeline components and route them to a handler. APIs can use standardized problem-details responses so clients receive a consistent status and safe explanation. A correlation or trace identifier can connect that response to internal diagnostics without exposing the diagnostic contents themselves.
I place exception handling early enough to cover the relevant pipeline. Once response headers or body data have already been sent, replacing the response is restricted, so streaming failures need special consideration. I also avoid catching every exception and returning success, since that conceals failures from clients and monitoring. Error handling should preserve accurate HTTP semantics and must itself avoid throwing or recursively failing.
31. How do you write useful application logs?
I write logs that explain meaningful application events and failures, using structured message templates rather than building every message with string concatenation. Structured fields let an operator filter and aggregate events without parsing human-readable sentences.
With an injected ILogger<OrdersController> named logger, this statement records an order identifier as a property:
C# example
The surrounding code must define orderId; the example demonstrates a logging statement, not an entire controller. For exceptions, I pass the exception object to the appropriate logging overload so the provider can retain diagnostic details.
I choose levels based on operational meaning and avoid treating every normal request as a warning. I also exclude passwords, bearer tokens, authentication cookies, and unnecessary personal data. Logging an entire request body can leak secrets and create excessive volume. Correlation identifiers help connect related events, but logging is not a replacement for metrics or traces. Useful logs answer what happened, to which operation, and why it matters without becoming a second uncontrolled data store.
32. Why should web request handlers use asynchronous I/O?
Asynchronous I/O allows a request to wait for a database, network, or file operation without occupying a thread for the whole wait. This helps the server handle more concurrent work with its available threads. It does not inherently make the external operation faster.
I use the asynchronous APIs supplied by the dependency and await them through the call chain. Calling .Result or .Wait() on an asynchronous operation blocks a thread and can contribute to thread-pool starvation under load. Wrapping ordinary blocking I/O in Task.Run inside each request is not equivalent to using a genuinely asynchronous API; it usually just moves the blocking work to another thread.
CPU-bound work still consumes CPU even when its method returns a Task. Large CPU jobs may need a bounded worker system instead of being hidden inside request handlers. I also await necessary work before the request completes rather than launching untracked fire-and-forget tasks that capture request-scoped services. Async improves resource use when applied to the real waiting boundary, not when added as a cosmetic keyword.
33. How do request cancellation tokens help an ASP.NET Core application?
Request cancellation allows work to stop when the client disconnects or the server otherwise aborts the request. ASP.NET Core exposes the signal through HttpContext.RequestAborted; supported endpoint and controller parameters can receive the corresponding cancellation token directly.
I pass that token into cancellable downstream operations, such as HTTP calls or database queries, rather than accepting it and ignoring it. Cancellation is cooperative: setting the token does not forcibly terminate arbitrary code, and a dependency must observe the token to stop promptly.
I distinguish an abandoned request from an ordinary server failure when logging. At the same time, cancellation does not undo work that has already committed. If an order was stored before the client disconnected, the application needs a durable operation identifier and appropriate retry semantics rather than assuming the transaction vanished. Request-aborted tokens also are not suitable as the lifetime policy for durable background jobs, which should have their own ownership and cancellation rules. For CPU loops or multi-step work, explicit checks may be needed between safe cancellation points.
34. How are static files served?
Static files are resources such as stylesheets, images, and JavaScript that can be returned without running application business logic. ASP.NET Core commonly uses a web root named wwwroot, which should contain only files intended to be publicly served under the configured policy.
ASP.NET Core 10 supports MapStaticAssets for build-discovered static assets with framework optimizations. UseStaticFiles remains relevant for scenarios such as serving files through configured file providers or other requirements outside that asset pipeline. I select the mechanism according to the application template and deployment rather than treating the two methods as identical.
Static files served before authorization middleware can be public even if controller endpoints require authentication. Private exports and user uploads therefore should not simply be copied into a public web root. A protected download should enforce authorization and control the returned content type and disposition. I also avoid exposing source files, configuration, or secret material, and I check cache headers because a sensitive response cached by a client or intermediary can outlive the request that produced it.
35. What is the difference between ViewData, ViewBag, and a strongly typed view model?
A strongly typed view model gives a Razor view an explicit data contract through its @model declaration. Its properties are discoverable and checked by the compiler, which makes it the preferred choice for the main data a view needs.
ViewData is a dictionary that carries values from a controller to a view. ViewBag provides dynamic access to the same underlying view-data collection, so they are not independent stores. Both can be convenient for small supplementary values, but spelling mistakes and incorrect value types are easier to miss than with a typed model.
These mechanisms support rendering the current response; they do not automatically preserve values across a redirect. A redirect causes a new request, so data must be retrieved again or carried through an appropriate temporary or persistent mechanism. I also distinguish a view model from a database entity: the view model should expose only the fields the interface needs. Strong typing improves maintainability, but it does not by itself validate input, enforce authorization, or prevent a developer from outputting unsafe content.
36. How does TempData support a redirect workflow?
TempData carries a small amount of data between requests, commonly a confirmation message after a successful form submission. A POST handler can save the change, set a message, and redirect to a GET page that reads and displays it. This supports the post-redirect-get pattern without placing the message in a permanent database record.
Reading a value normally marks it for removal at the end of the request. Peek reads without marking it for deletion, while Keep retains a value that would otherwise be removed. The actual storage depends on the configured provider; ASP.NET Core commonly uses the cookie-based provider, and a session-based provider is also available.
I keep TempData small and avoid treating it as durable workflow state. A user can abandon the redirect, open multiple tabs, or lose the associated client state. Large objects are a poor fit, especially with cookie storage limits and request overhead. Although the provider can protect its payload, the destination page still needs correct output encoding, and critical business decisions should rely on authoritative server-side records rather than a temporary message.
37. What is the Web Forms page lifecycle in classic ASP.NET?
In classic ASP.NET Web Forms on .NET Framework, a page passes through a server-side lifecycle that builds and processes its control tree. Major stages include initialization, restoring state and postback data, loading, handling postback events, preparing to render, saving state, rendering, and unloading.
The order matters. Dynamic controls need to be recreated early with stable identifiers so restored state and posted values can be applied correctly. Code that unconditionally rebinds controls during Page_Load can overwrite values or interfere with later event handling. Rendering produces HTML for the browser; after that, the server-side page instance is not a permanently running object attached to the browser tab.
This is explicitly a Web Forms model, not the ASP.NET Core middleware lifecycle. Core controllers, Razor Pages, and Blazor components do not implement System.Web.UI.Page or automatically run Web Forms page events. When maintaining a legacy page, I reason about where state restoration and control events occur instead of moving initialization code between events until the symptom disappears. Understanding that order is especially important for dynamic forms and reusable server controls.
38. What are postback and IsPostBack in Web Forms?
In classic ASP.NET Web Forms, a postback sends form data back to the server for page processing, often in response to an action involving a server control. The page's IsPostBack property indicates whether the request is being processed as a postback rather than an initial page request.
A common legacy pattern initializes a list only on the first request:
C# example
This excerpt belongs to a Web Forms page derived from System.Web.UI.Page; LoadInitialChoices is an application-defined method. It is not ASP.NET Core controller or Razor Pages code.
The guard prevents unnecessary initialization from overwriting state on later postbacks, but it is not a universal rule for every binding operation. Dynamic controls often must be recreated on each request early enough for state restoration, and some data should deliberately refresh. IsPostBack is also not an authentication or CSRF check. A request being a postback says nothing about whether the caller is authorized to perform its operation.
39. What is ViewState, and is it the same as session state?
ViewState is a classic Web Forms mechanism for preserving selected page and control state between requests. The framework serializes that state into hidden fields sent to the browser and receives it again on postback. Session state instead keeps data on the server and associates requests with a session identifier.
ViewState can simplify control behavior but increases response and request sizes. I disable it selectively where state does not need to be preserved and verify that required control behavior still works. Control state is a related but separate mechanism for essential control information and can remain even when ViewState is disabled.
ViewState is not confidential merely because its representation is encoded. Integrity protection helps detect tampering; encryption is a separate concern. I do not put secrets into it or disable its security protections to resolve configuration problems. In a server farm, protection configuration must work consistently across the relevant servers. None of this describes an ASP.NET Core feature: Razor Pages and MVC views do not automatically carry a Web Forms ViewState payload between requests.
40. How do Global.asax, HTTP modules, and web.config relate to ASP.NET Core?
Global.asax, HTTP modules, and much of classic ASP.NET's web.config application configuration belong to the System.Web hosting model. Global.asax exposes application-level events, while HTTP modules participate in the classic request pipeline. An HTTP handler produces a response for a selected request type.
ASP.NET Core uses its host, service registrations, middleware, and endpoints for comparable responsibilities. Startup is usually composed in Program.cs, configuration comes through providers, and middleware wraps request processing. A migration should map the responsibility of a module or event handler to the appropriate Core abstraction rather than copy the old API calls unchanged.
A web.config file can still appear in a Core application hosted on IIS, where it configures IIS and the ASP.NET Core Module. Its presence does not mean Core uses System.Web or reads every classic ASP.NET setting. I separate host configuration from application configuration and check which component consumes each value. Replacing the framework often requires redesigning dependencies on session events, static context access, or other assumptions tied to the old pipeline.
ASP.NET Interview Questions and Answers for Intermediate
These answers explain practical implementation details and their trade-offs, including version-sensitive behavior and the setup assumed by each example.
41. How do you choose the order of middleware in ASP.NET Core?
Middleware order determines what a component can observe and which later components it can protect. Requests move forward through the pipeline; code after await next(context) runs on the way back. An exception handler must wrap the work whose failures it handles, and authorization needs the identity produced by authentication and the metadata selected by routing.
For an app that has already registered the corresponding services and authentication scheme, this is a useful explicit ordering excerpt:
C# example
UseExceptionHandler() here assumes an exception-handling mechanism such as registered Problem Details services. The policy name must also exist. Forwarded-header processing, if needed behind a trusted proxy, belongs earlier than middleware that consumes the original scheme or host.
There is no universal list suitable for every application. Static files served before authorization may be public, endpoint-specific rate limiting needs selected endpoint metadata, and caching changes which downstream work executes. Minimal hosting can add some middleware automatically; explicit calls are useful when ordering dependencies must be controlled.
42. How do Map, MapWhen, and UseWhen change the request pipeline?
Map creates a branch selected by a path prefix. The matching prefix moves from Request.Path into Request.PathBase while that branch executes, which matters when generating links or inspecting paths. MapWhen selects a separate branch using a predicate rather than a path prefix.
UseWhen also selects work using a predicate, but its branch normally rejoins the main pipeline when that branch does not terminate the request. It is useful for conditional diagnostics or headers that should not become a separate application endpoint.
C# example
This excerpt adds a response header for matching paths and then allows normal processing. If the branch instead used terminal middleware without calling the next delegate, the rest of the request pipeline would not run.
I use branching sparingly because it can accidentally bypass authentication, error handling, or observability. MapGet and related endpoint-mapping methods are routing registrations, not the same operation as path-prefix middleware branching with Map.
43. How do you return consistent errors with exception handling and Problem Details?
I centralize unexpected failures in exception-handling middleware and return a stable Problem Details contract rather than exposing exception messages or stack traces. Known business failures should usually be translated deliberately into appropriate results, such as a conflict or not-found response, instead of being thrown solely to drive HTTP control flow.
A small ASP.NET Core setup is:
C# example
The registered Problem Details service can supply bodies for supported error responses when the request's accepted content types are compatible with its writer. Status-code pages address otherwise empty error responses; exception handling addresses failures. They are not interchangeable.
For production, I log diagnostic details on the server with a correlation identifier and keep the public response sanitized. I test validation errors, authorization failures, missing routes, and unhandled exceptions separately: adding these services does not guarantee every existing custom error body is replaced. Once a response has started, middleware cannot reliably replace it with a new error document.
44. How do you validate strongly typed configuration before serving requests?
I bind configuration to an options type and validate the values as part of application startup. This turns a missing or invalid setting into an immediate deployment failure instead of a sporadic error on the first request that happens to use it.
For this example, define DeliveryOptions in the app and register its options before building the host:
C# example
The class declaration belongs after top-level statements or in its own file. A missing timeout binds to zero and therefore fails this rule. More complex validation can use IValidateOptions<T>; DataAnnotations validation requires its appropriate extensions.
Validation should describe actual invariants, not only whether a string is nonempty. I also avoid logging secret values in validation errors. Startup validation cannot guarantee that a remote endpoint is reachable forever or that a later configuration reload remains valid; those are separate operational and reload-handling concerns.
45. How do IOptions, IOptionsSnapshot, and IOptionsMonitor differ?
IOptions<T> exposes a cached options value and does not provide live configuration updates. It is suitable for stable settings and can be injected into any service lifetime. IOptionsSnapshot<T> is scoped: it computes and caches named options within a scope, so ordinary web requests can see refreshed settings on later requests without changing values halfway through one request.
IOptionsMonitor<T> is a singleton service that exposes current values, named options, and change notifications. It fits singleton consumers and applications that intentionally react to changes. Reload behavior still depends on the configuration provider: a provider that never reloads cannot produce dynamic updates merely because the consumer uses a monitor.
I avoid injecting a snapshot into a singleton because that violates the lifetime relationship. I also decide whether a long-running operation should capture one validated configuration version or reread values during execution. Mixing a new endpoint address with an old credential can be worse than applying the new configuration at a clear boundary.
A change callback is not a transaction across all application state. Validate reloadable values, dispose subscriptions when appropriate, and define what happens if a new configuration is invalid.
46. What is a captive dependency, and how do you avoid one?
A captive dependency is a shorter-lived service retained by a longer-lived consumer. The classic mistake is a singleton holding a scoped service: that scoped object can effectively survive for the singleton's lifetime, bringing stale request state, incorrect disposal, or concurrent access to a service that was never designed for it.
I fix the ownership model rather than merely suppressing container validation. A request-specific consumer can become scoped. A singleton can depend on a suitable singleton abstraction. For genuinely separate units of work, a background service can create a scope through IServiceScopeFactory, resolve the scoped service inside it, await the work, and dispose the scope.
Scope validation catches important registrations, especially scoped services resolved from the root provider, but it does not prove all dependencies are thread-safe. A transient object retained by a singleton can still become long-lived and shared.
I avoid calling BuildServiceProvider during registration to create a second container. That can duplicate singletons and complicate disposal. Service lifetimes express ownership and concurrency assumptions, not just how frequently the constructor runs.
47. How can middleware safely use a scoped service?
Conventional middleware is constructed for the application pipeline, so I do not inject a request-scoped dependency into its constructor. Instead, I request that dependency as an additional InvokeAsync parameter. ASP.NET Core resolves it from the active request's services.
C# example
Register RequestAudit with AddScoped<RequestAudit>() and add the middleware with app.UseMiddleware<AuditMiddleware>(). Other scoped consumers in the same request then receive the same RequestAudit instance. This example stores only a path; production audit data should avoid sensitive query parameters and bodies.
Factory-activated middleware implementing IMiddleware offers another model: registering the middleware with an appropriate scoped or transient lifetime supports scoped constructor dependencies. In either model, I avoid retaining HttpContext or request-scoped objects after the request completes, and I await asynchronous work before that lifetime ends.
48. How do route groups simplify a Minimal API?
A route group applies a common prefix and endpoint conventions to related Minimal API endpoints. It reduces repeated authorization, tags, filters, and other metadata while keeping individual handlers focused on their own operation.
C# example
This excerpt assumes authentication and the OrdersAccess policy have already been configured. The integer route constraint influences route matching; it is not a complete validation or resource-access check. Returning an order by identifier still requires authorization for the specific order when ownership or tenant boundaries matter.
Groups can be nested to express hierarchy, but I keep the resulting route structure understandable and test inherited conventions. A group does not create its own DI scope, database transaction, or isolated middleware pipeline. It also does not guarantee that every endpoint is safe just because authorization metadata was attached. Endpoint-specific anonymous access, additional policies, and handler behavior still need explicit review.
49. When should you use an endpoint filter instead of middleware or an MVC filter?
I use middleware for concerns that operate on HTTP requests across the pipeline, such as broad diagnostics or exception handling. I use a Minimal API endpoint filter when the concern needs the selected handler's arguments or result, such as validating a parsed argument or recording handler-specific timing.
MVC action filters belong to the controller action pipeline. They have MVC-specific contexts and lifecycle stages, so an MVC filter is not automatically a Minimal API filter. Likewise, an endpoint filter should not be assumed to run around every controller action merely because controllers are mapped as endpoints.
An endpoint filter receives an invocation context and a delegate for the next filter or handler. It can inspect arguments, await the next stage, change a result, or short-circuit execution. Binding must already have produced the relevant arguments, which is an important difference from early middleware.
I keep authorization in the authorization system rather than building ad hoc permission checks into a generic filter. I also test filter order and short-circuit behavior: a logging filter that never calls the next delegate can accidentally prevent the endpoint from executing.
50. How do binding sources and request-body reading affect endpoint design?
Binding sources determine where endpoint parameters come from: route values, query strings, headers, form fields, JSON bodies, or DI. I make ambiguous contracts explicit with attributes such as [FromRoute], [FromQuery], and [FromBody], rather than relying on a parameter name that may later change.
For controller APIs, [ApiController] enables binding-source inference, but a complex type is not always a body DTO: registered services can be inferred as DI parameters. Minimal APIs have their own inference rules and support framework types, services, simple parsable values, and JSON body parameters.
The request body is a forward-reading stream by default. I use one body DTO instead of expecting two separate parameters to deserialize the same JSON document independently. Logging or signature-verification middleware that consumes the body must deliberately enable buffering when appropriate, respect size limits, and rewind it before downstream processing.
Binding failures and business validation are different. An unparseable integer is a representation problem; a parsed quantity exceeding the customer's allowance is a domain rule. I test missing, malformed, and unexpected input separately, including content-type mismatches.
51. How does automatic request validation differ between controllers and .NET 10 Minimal APIs?
Controller APIs marked with [ApiController] normally return an automatic HTTP 400 response when model validation fails. Ordinary MVC actions and Razor Pages still need the appropriate ModelState.IsValid handling for their workflow; adding attributes to a model does not automatically choose the user-facing response everywhere.
ASP.NET Core 10 adds built-in Minimal API validation that is enabled by registering validation services. With the appropriate Microsoft.Extensions.Validation package referenced, this example enables supported parameter validation:
C# example
The validation infrastructure uses generated metadata, so discovery and assembly boundaries matter. I do not claim this registration retroactively describes the behavior of older Minimal API versions, where manual checks, custom filters, or libraries were common.
These checks validate shape and declared constraints. They do not replace resource authorization, uniqueness enforcement, or business rules that depend on stored state. I test invalid requests through the real HTTP pipeline rather than only invoking the handler directly.
52. How do request DTOs prevent overposting?
A request DTO defines exactly which fields a client may submit for a particular operation. This prevents a request from directly assigning sensitive persistence fields such as an owner identifier, approval status, or administrative flag. I map the permitted values onto an existing entity after loading it and checking authorization.
C# example
This is a contract sketch, not a complete update implementation: validate the name, enforce resource access, and persist through the application's data service. Separate create, update, and response DTOs are often clearer than one object reused for every purpose.
A DTO does not automatically make every included property safe. A client-supplied price, discount, or account identifier may still require independent verification. I also distinguish fields the client omitted from fields explicitly set to null when designing partial updates.
I do not rely on MVC binding exclusions as the main protection for JSON request bodies because input formatters have their own behavior. Explicit contracts and explicit mapping make allowed changes visible in code review.
53. How do you handle file uploads safely?
I treat an uploaded file as untrusted content, not as a trusted filename plus bytes. I set request and application size limits, restrict accepted formats, verify the content when practical, generate a server-controlled storage name, and store the file outside executable or publicly served application directories.
IFormFile is convenient for buffered multipart uploads. Large uploads may need a streaming design to avoid excessive memory or temporary-disk pressure. These approaches have different operational limits, so increasing a server limit alone is not a safe large-file strategy.
I never combine a client filename directly with a storage path. A filename can contain traversal attempts, misleading extensions, or HTML characters; even when shown only in an upload confirmation, it must be encoded. MIME headers and extensions are hints, not proof of file safety. Malware scanning and quarantine may be necessary before a file becomes downloadable.
Cookie-authenticated upload forms need antiforgery protection, and every upload needs authorization, quotas, and cleanup of abandoned data. I return safe download headers and avoid serving active content in a context where it can run with the application's origin privileges.
54. How do JSON settings and content negotiation differ between controllers and Minimal APIs?
Controller APIs use MVC input and output formatters. Their response negotiation can consider the request's Accept header and configured formatters; JSON is common, but XML or other formats require explicit support. Options such as returning HTTP 406 for an unsupported requested format are application choices rather than universal defaults.
Minimal API results use their own result execution. Results.Json explicitly writes JSON and does not acquire MVC's formatter negotiation simply because controllers exist in the same app. TypedResults additionally exposes concrete result types that can improve compile-time clarity and endpoint metadata.
The JSON configuration paths are also distinct:
C# example
The first configures MVC's System.Text.Json integration; the second configures the HTTP JSON options used by Minimal APIs. This excerpt deliberately preserves .NET property names instead of the usual web naming policy, so it is a contract change that should be tested with clients.
I keep serialization contracts stable and avoid returning persistence entities whose navigation properties, cycles, or internal fields can accidentally change the public API.
55. How do Razor Pages handlers and Post-Redirect-Get work together?
Razor Pages groups request handling around a page. Methods such as OnGet, OnPost, and OnPostDeleteAsync are selected by the HTTP method and, when present, the handler name. A named handler is not a separate controller action; it participates in the page's request model.
Post-Redirect-Get means that after a successful state-changing POST, the handler redirects to a GET page. Refreshing the resulting page then repeats the GET rather than resubmitting the original form. On validation failure, I normally return Page() so the same request can render validation errors and the submitted values.
C# example
This is a PageModel method excerpt; actual persistence must complete successfully before redirecting. A redirect is not an idempotency guarantee: a double click or network retry can still submit the original POST more than once. For operations such as payments or order creation, I add an appropriate server-side duplicate-submission strategy.
I use TempData only for small cross-request messages, not as the authoritative record of the operation.
56. How do you protect browser form submissions against CSRF?
CSRF protection matters when a browser automatically attaches credentials, such as authentication cookies, to a request. An attacker can try to make that browser submit a state-changing request from another site. An antiforgery mechanism requires additional evidence that the legitimate application generated the request, commonly a token submitted with the form or in a configured header.
ASP.NET Core Razor Pages provides antiforgery protection for its normal form workflow, and the MVC Form Tag Helper can generate tokens for suitable POST forms. MVC endpoints still need the relevant validation policy, such as AutoValidateAntiforgeryToken for unsafe methods. I test the rendered form and server validation together; generating a token without validating it provides no protection.
Minimal API form endpoints, including supported file-binding scenarios, have their own antiforgery integration. Configure the services and middleware required by the endpoint instead of assuming MVC filters apply to it.
SameSite cookies and origin checks can add protection but are not a reason to remove the intended token workflow blindly. I never make GET requests change state. A bearer-token API that accepts only an explicitly supplied authorization header has a different CSRF exposure, but storing or transporting that token in an automatically attached cookie changes the threat model.
57. What does CORS allow, and what does it not protect?
CORS controls whether browsers allow a page from one origin to read responses from another origin. The server sends the allowed-origin, method, header, and credential information; the browser enforces that policy. A preflight request may ask permission before the actual request is sent.
CORS is not authentication, authorization, a firewall, or protection against nonbrowser clients. A caller using an HTTP client can still send requests, and some cross-origin browser requests can be sent even when JavaScript cannot read the response. Sensitive operations therefore still need authentication, authorization, and appropriate CSRF defenses.
I allow specific trusted origins, including their scheme and port, rather than reflecting arbitrary origins. Credentialed browser access requires particular care: a wildcard origin cannot be combined with credentials as a valid allow-everyone policy. Origins identify applications, not individual users.
In ASP.NET Core, I register a named policy and apply it through middleware or endpoint metadata consistently. When endpoint routing is used, the middleware must run where it can see the selected endpoint and before authorization blocks the preflight workflow. I diagnose CORS with the actual browser request, preflight response, and response headers rather than adding permissive settings until the error disappears.
58. When do you choose cookie authentication instead of bearer authentication?
Cookie authentication fits a browser application where the server maintains a sign-in session using an authentication cookie. Bearer authentication fits clients that explicitly attach an access token to each request, such as APIs used by mobile applications or service clients. The choice depends on the client and security architecture, not on whether the endpoint returns HTML or JSON.
A cookie is normally attached automatically by the browser, so state-changing requests need CSRF consideration. A bearer token is usable by whoever possesses it; it needs secure transport, careful storage, correct issuer and audience validation, and a limited lifetime. Decoding a JWT is not the same as validating it, and not every bearer token is a JWT.
I use established identity protocols and libraries instead of inventing password or token formats. For a browser frontend, a backend-for-frontend design can keep access tokens out of browser JavaScript while maintaining an application cookie.
In ASP.NET Core 10, known API endpoints protected by cookie authentication generally return 401 or 403 for challenges or forbids instead of redirecting to HTML login pages. I verify endpoint metadata and configuration rather than assuming every cookie-protected route has identical behavior.
59. How do you express authorization with policies and claims?
An authorization policy gives a named, reusable meaning to access rules. It can require an authenticated user, particular claims, roles, or custom requirements evaluated by authorization handlers. Endpoint code then refers to the policy rather than scattering string comparisons throughout the application.
C# example
The permission claim must come from a trusted, validated identity source. Accepting a request body field with that name would not establish permission. Claim names, values, and mapping behavior are part of the identity contract and should be tested explicitly.
Multiple requirements in one policy must all succeed. More complex alternatives may require a custom requirement or assertion rather than piling up requirements and expecting OR behavior. Policies also do not automatically check ownership of an individual database record. I combine endpoint-level permission with resource-based checks when the operation depends on which record is being accessed.
60. How do you authorize access to an individual resource?
I load the resource or the minimum authorization-relevant representation, then ask IAuthorizationService whether the current user may perform the requested operation on it. An attribute such as [Authorize] runs before the action has loaded that record, so it cannot by itself evaluate arbitrary ownership or tenant state discovered later.
C# example
This excerpt assumes the controller already requires authentication and that the policy has a handler capable of evaluating this document type. The policy name alone does not create that handler. If authentication is not guaranteed, distinguish an authentication challenge from forbidding an already authenticated user.
I also consider information disclosure: some applications deliberately return not-found for inaccessible records. Query-level tenant scoping can reduce accidental exposure, but it does not eliminate operation-specific authorization. Permission-sensitive state may change between checking and writing, so the persistence design must enforce any required consistency.
61. How should ASP.NET Core session be used in a scaled application?
Session stores temporary per-browser-session data on the server, with a cookie identifying the session. In ASP.NET Core, it uses an IDistributedCache implementation, but the configured implementation determines whether the data is actually shared across machines. An in-memory distributed-cache implementation is still local to one process.
For multiple instances, I use an appropriate shared store and shared Data Protection configuration so requests can move between instances without losing access to protected cookies. Session middleware must run before endpoints that use it. The session cookie is not an authentication mechanism, and a session identifier is not proof of a user's permissions.
I keep session small and disposable. Important records such as orders belong in durable application storage, because session can expire or disappear. Accessing session asynchronously through LoadAsync can avoid a synchronous store load in applicable paths.
ASP.NET Core session is non-locking: simultaneous requests can overwrite one another's changes. I do not use it as a concurrency-safe shopping-cart database or compare it directly with legacy System.Web session-locking behavior. Where updates matter, I store authoritative state with explicit concurrency control and use session only for navigation or convenience state.
62. How do memory caching and distributed caching differ?
IMemoryCache keeps objects inside one application process. It is fast and can store ordinary .NET objects, but another instance has a different cache and a process restart loses its entries. It suits local reusable data when inconsistency between instances is acceptable or explicitly managed.
IDistributedCache provides a shared-cache abstraction with byte-oriented values. A real distributed provider can make entries available across instances, but it adds serialization, network latency, capacity management, and another failure mode. Registering the in-memory implementation of that interface does not make it a cross-machine cache.
C# example
The excerpt requires Microsoft.Extensions.Caching.Distributed and assumes a provider is registered. The value is intentionally trivial; real serialized contracts need versioning and size limits.
Neither API makes cached data authoritative or automatically prevents simultaneous cache misses from triggering duplicate work. I define expiration, invalidation, fallback behavior, and what may safely be served stale before selecting the storage mechanism.
63. How does output caching differ from response caching?
Response caching follows HTTP caching semantics, including request and response cache headers. It is appropriate when the resource can be cached according to those semantics by clients or intermediaries. An MVC [ResponseCache] attribute primarily configures headers; it is not itself a general-purpose server output store.
Output caching is a server-controlled policy for reusing generated responses and reducing repeated endpoint execution. ASP.NET Core provides output-cache services, middleware, and endpoint policies. Its default policy caches HTTP 200 responses to GET or HEAD requests but avoids authenticated requests and responses that set cookies.
C# example
This excerpt is intentionally public, stable data. In a real pipeline, place output caching after routing and after authentication and authorization when those are used. Add a suitable expiration and variation policy for the actual endpoint.
I do not override identity-related safeguards simply to improve hit rate. A cached response that crosses user or tenant boundaries is a data leak. Output caching also differs from caching database lookup results: one reuses an HTTP response, while the other reuses application data.
64. How do cache keys and invalidation prevent incorrect responses?
A cache key must identify every dimension that can change the cached value. Depending on the resource, that can include tenant, locale, representation format, selected query parameters, and a contract version. Omitting one dimension can return a technically valid value to the wrong caller or for the wrong representation.
I avoid inserting arbitrary unbounded user input directly into a cache namespace without normalization and limits. A public search endpoint with limitless unique query strings can fill a cache without providing useful reuse. I also avoid caching personalized responses unless the entire isolation and authorization model is deliberately designed for it.
Invalidation should follow the consistency requirement. A short expiration may be enough for a public summary. A product update may require evicting a related cache entry or a tagged group of output entries. Versioned keys can support predictable changes, but obsolete entries still consume storage until removed or expired.
I invalidate only after the authoritative write succeeds and consider races between readers and writers. Evicting immediately before committing can allow another request to repopulate stale data. For stronger guarantees, I use a coordinated publication or versioning design rather than claiming expiration alone provides consistency.
65. Why use IHttpClientFactory for outbound HTTP requests?
IHttpClientFactory centralizes outbound-client configuration and manages reusable message handlers. Named or typed clients can define base addresses, default headers, logging, and delegating handlers for a particular dependency without repeating configuration across request handlers.
The factory creates HttpClient objects while pooling the underlying handlers. That supports connection reuse and handler rotation; it is not the same as constructing and disposing a fresh socket pool for every request. I usually request a short-lived factory client or appropriately scoped typed client and avoid capturing a typed client indefinitely inside a singleton.
I pass per-request tokens and other user-specific information through the outgoing request rather than mutable shared default headers or handler fields. Handler DI scopes differ from incoming ASP.NET Core request scopes, so a scoped service inside a handler is not automatically the same request-scoped object seen by a controller.
Cookie-based outbound integrations need special care because pooled handlers can share CookieContainer state and rotation can discard it. The factory is not automatically suitable for that workflow. A deliberately managed long-lived client with a configured connection lifetime is another valid approach; the important goal is controlled connection and state lifetimes, not using one API everywhere.
66. How do you add timeouts and retries without duplicating side effects?
I set a total time budget for the outbound operation and decide which failures are safe to retry. A timeout says the caller did not receive a timely result; it does not prove the remote service did nothing. Retrying a payment or order creation without an idempotency contract can therefore duplicate the side effect.
For safe reads, a bounded retry policy with backoff and jitter can help with transient failures. I respect throttling guidance where applicable and avoid retrying permanent errors such as invalid credentials or malformed requests. For writes, I either disable automatic retries or use a stable idempotency key whose server-side semantics are defined.
Modern .NET HTTP resilience libraries can compose timeouts, retry, circuit-breaking, and other strategies with factory clients, but their defaults must be reviewed. Multiple retry layers can multiply attempts and exceed the caller's deadline.
I propagate the request's cancellation token where appropriate and distinguish cancellation from an upstream timeout in diagnostics. A circuit breaker reduces repeated calls to an unhealthy dependency; it does not repair the dependency or provide a fallback automatically. I test the number of attempts and observed duration, not just whether a successful response eventually appears.
67. How do you keep database work bounded to a request?
I keep a database unit of work short, explicit, and owned by the operation that needs it. In a web application, a scoped data service often aligns with one request, but the DI scope itself does not start a transaction or make all database calls atomic.
For a write that changes several related records, I use a transaction when those changes must succeed or fail together. I avoid holding that transaction open while waiting for a remote HTTP service or for the user to take another action. Long transactions increase locking, connection occupancy, and failure recovery costs.
I use asynchronous database APIs with cancellation support, parameterized queries, bounded result sets, and a deliberate timeout policy. A data-access object that is not thread-safe must not be shared across parallel work simply because each method returns a task. Request-scoped does not mean concurrency-safe.
The response should reflect the actual persistence outcome, not merely that a write was queued in memory. When a database change must trigger an external message reliably, I consider an outbox or another consistency mechanism instead of pretending one local transaction covers the remote system. These boundaries matter regardless of whether the application uses an ORM or direct SQL.
68. How do you handle concurrent updates through an HTTP API?
I detect whether the resource changed since the client read it, rather than silently accepting a last-write-wins update when that would lose important work. A database concurrency token can protect the write, while an HTTP ETag and If-Match header can expose a version precondition to clients.
The client sends the version it edited. The server compares or enforces that version during the update and rejects a stale write. If the If-Match precondition fails, HTTP 412 is the natural response; a separate domain conflict may justify 409. The distinction should be documented rather than using either code at random.
A check performed only before the database write has a race: another writer can change the record between the check and save. The persistence operation must enforce the concurrency condition atomically or use an appropriate transaction and isolation strategy.
I return enough safe information for the client to reload or resolve the conflict, but I do not automatically retry a stale user edit against the latest version. That can overwrite the very change the check was intended to protect. For partial updates, I still define which fields and invariants are being protected.
69. How do you avoid blocking and unnecessary parallelism in a request handler?
I await asynchronous I/O instead of blocking on .Result or .Wait(). ASP.NET Core usually does not have the legacy ASP.NET synchronization context that explains some classic deadlocks, but synchronous blocking still occupies request-processing threads and can cause thread-pool starvation under load.
Task.Run is not a general fix for slow database or network calls. It schedules work on another thread-pool thread without making synchronous I/O inherently scalable. For CPU-intensive work, I measure the cost and consider a bounded worker or separate processing service rather than allowing every request to create unlimited parallel work.
Parallelizing independent I/O can reduce latency, but only when the dependencies, rate limits, and data-access objects support that concurrency. I do not call Task.WhenAll on several operations sharing a non-thread-safe data context. More parallel calls can also overwhelm an upstream service and worsen total latency.
I pass cancellation tokens, limit fan-out, and preserve an overall deadline. Performance testing should include concurrent traffic and tail latency: a handler that looks fast for one request can be fragile when hundreds of requests compete for threads, connections, or remote quotas.
70. How do SignalR hubs differ from ordinary request-response endpoints?
SignalR provides a higher-level connection model for real-time server-to-client and client-to-server communication. A hub exposes methods that connected clients can invoke and can send messages to clients, users, or groups. It abstracts supported transports, but it does not turn the connection into a durable message queue.
C# example
Register SignalR with builder.Services.AddSignalR() and map the hub with app.MapHub<UpdatesHub>("/updates"). This example joins only a public group; private subscriptions need authorization based on server-verified information.
Hub instances are transient, so I do not store durable connection or application state in hub instance fields. Code outside a hub uses an injected IHubContext<UpdatesHub> rather than constructing a hub manually. I await sends and define what the client should do after a disconnect, because a missed real-time notification may require reloading authoritative state.
Ordinary HTTP endpoints remain appropriate for commands and queries that benefit from simple request-response semantics. I use SignalR when timely push communication actually improves the workflow.
71. How do you secure SignalR connections and groups?
I authenticate the connection and authorize access to the hub and any sensitive hub methods. A connected user is not automatically allowed to join every group or invoke every operation. Group names supplied by clients are untrusted input, so the server verifies membership before adding a connection to a private group.
SignalR groups are routing conveniences, not a replacement for the authorization model. Membership can change, connections can reconnect, and applications must decide how permission revocation affects an existing connection. I reestablish permitted subscriptions through a controlled workflow rather than treating a remembered group name as proof of access.
Browser transport limitations can require access tokens to appear in the query string for some SignalR connections. HTTPS protects transport, but infrastructure logs can still capture the URL, so I restrict token reading to the intended hub path and avoid logging sensitive query strings.
I also consider token expiration during a long-lived connection, connection limits, message sizes, and method-level validation. Authentication at connection establishment does not mean every later business operation is permanently authorized. For sensitive actions, I check current permissions against authoritative state and define whether expired or revoked sessions should be disconnected.
72. How do Blazor Web App render modes change where code runs?
In a Blazor Web App, static server-side rendering produces HTML without an interactive component connection. Interactive Server executes component logic on the server and communicates UI events over a circuit. Interactive WebAssembly executes interactive component code in the browser after the required client assets load.
Interactive Auto initially uses server interactivity when appropriate and can use WebAssembly on later visits after the client bundle is available. It does not seamlessly migrate an already running component instance from server memory into the browser in the middle of a session.
Razor example
This component assumes a Blazor Web App configured for interactive server components and the usual RenderMode static import. A standalone .razor file does not enable those host services or endpoints itself.
The render mode changes DI availability, latency, state lifetime, and where secrets may safely exist. Browser-executed code is visible to the user and must call protected server APIs for privileged operations. I choose modes by component requirements and deployment constraints rather than assuming all Razor syntax runs in the same place.
73. Why can Blazor component initialization run more than once?
An interactive component can first be prerendered on the server and then initialized again when the interactive runtime starts. Those are separate component instances, so code in initialization may execute twice. This is expected lifecycle behavior, not automatically a framework bug or duplicate user action.
I keep initialization safe to repeat and avoid placing irreversible business operations in it. Loading display data is different from creating an order or sending an email. The latter belongs behind a deliberate authorized command with duplicate protection.
For data that should survive the prerender-to-interactive transition, I use the framework's supported persistent component-state mechanisms and avoid serializing secrets into client-visible state. Alternatively, I can disable prerendering where its benefits do not justify the lifecycle complexity. The correct choice depends on startup experience, data sensitivity, and render mode.
Browser-dependent JavaScript interop is another boundary: the browser DOM is not available during server prerendering. I schedule DOM-dependent work in an appropriate interactive render lifecycle, commonly after rendering, and guard first-render behavior when necessary. A guard within one instance alone does not suppress work performed by a separate prerendered instance.
74. Why is hiding UI with AuthorizeView insufficient for authorization?
AuthorizeView controls what a Blazor component displays for the current authentication state. It can hide a button or show different content, but it is not a security boundary for the underlying operation. A user can call an exposed server endpoint directly even if the UI never renders its button.
I protect server APIs with authentication and authorization and apply resource checks to sensitive operations. On WebAssembly, all client code and client-visible state must be treated as inspectable and changeable. A browser-side role check can improve the experience but cannot establish server-side permission.
For interactive server components, event handlers still need correct authorization design. A long-lived circuit may outlast changes to a user's account or permissions, so the application needs a suitable revalidation and revocation strategy rather than assuming the initial authentication state is permanent.
I use AuthorizeView to keep the UI consistent with the user's access, not as evidence that data cannot be retrieved. I also avoid sending sensitive data to the client and merely hiding it. The server should return only information the authenticated principal is authorized to receive, and tests should exercise direct unauthorized requests as well as the visible UI.
75. How do you test ASP.NET Core endpoints with WebApplicationFactory?
WebApplicationFactory<TEntryPoint> starts an ASP.NET Core application for integration testing and provides an HTTP client against its test host. It exercises routing, middleware, DI, binding, serialization, and endpoint execution together, which a direct handler call cannot do.
For a test project referencing the app, a matching Microsoft.AspNetCore.Mvc.Testing package, and xUnit, a basic test can look like this:
C# example
This assumes the app defines /status and exposes its entry-point type to the test project, for example with public partial class Program { } after top-level statements. A meaningful test should additionally assert the expected body and relevant headers.
I disable automatic redirects when testing login challenges or redirect targets, otherwise a final successful page can hide an incorrect intermediate response. The in-process host does not establish real proxy, TLS, browser, or deployment behavior; those require separate tests.
76. What belongs in a controller unit test versus an integration test?
A controller unit test verifies the action's own branching and interaction with its dependencies. I construct the controller with controlled substitutes, call the action, and assert the result or a meaningful side effect. It is useful for cases such as returning not-found when a service cannot locate a record.
Calling an action directly does not run routing, model binding, authorization middleware, filters, or JSON serialization. For example, a unit test cannot prove that [ApiController] automatically returns 400 for malformed input because that behavior occurs around the action, not inside the method call.
An integration test sends an HTTP request through a test-hosted application and checks that these components cooperate. I use it for route contracts, validation responses, authentication challenges, serialization, and important middleware behavior. A focused number of integration tests can verify boundaries that dozens of isolated action tests would miss.
I avoid asserting implementation details that make tests fail during harmless refactoring. A unit test should explain the action's decision; an integration test should explain the observable HTTP contract. Neither automatically proves the actual production database semantics, browser interaction, or reverse-proxy configuration unless those components are genuinely included in that test.
77. How do you replace infrastructure dependencies in an integration test?
I override the application's service registrations in a test-specific factory configuration, replacing external infrastructure with controlled implementations. The replacement should preserve the contract being tested without accidentally contacting production services.
C# example
This excerpt assumes an existing WebApplicationFactory<Program> factory and application/test types IEmailSender and RecordingEmailSender. Removing the original registrations avoids leaving an unexpected implementation available through IEnumerable<IEmailSender> or another resolution path. The recording implementation must be safe for the test's concurrency and lifetime.
I isolate test data, reset state between tests, and avoid a shared singleton recorder that makes parallel tests interfere. A fake authentication handler can test authorization paths, but it does not test a real identity provider or token validation.
For data-access behavior, I choose a provider that preserves the semantics under test. An in-memory fake does not establish relational constraints, transaction behavior, SQL translation, or production concurrency correctness. Those require a representative database test environment.
78. How do you customize model binding without breaking validation?
I customize binding only when the input representation genuinely needs it, such as turning a compact query value into a domain-specific value object. In MVC, a custom IModelBinder can read value providers, parse input, report binding errors, and set a result. A binder should not silently coerce malformed values into a plausible default.
For a simple reusable type, a supported TryParse pattern or type converter may be less invasive. Minimal APIs have their own customization mechanisms, including supported parsing patterns and BindAsync; an MVC binder is not automatically used by a Minimal API handler.
Binding translates external input into a value. Validation determines whether that value satisfies declared or business constraints. I preserve that distinction so error responses remain predictable. If parsing fails, I record or return the appropriate failure rather than letting unrelated business code receive a partially initialized object.
I test missing values, malformed values, culture-sensitive formats, repeated parameters, and excessive input lengths through the intended HTTP endpoint. A custom binder should avoid database calls or authorization decisions unless that responsibility is deliberately designed; otherwise a seemingly simple parse can introduce hidden latency, side effects, and security assumptions.
79. How do cancellation and streaming affect response design?
Cancellation signals that the request no longer needs work, but it is cooperative. I pass HttpContext.RequestAborted, or a bound CancellationToken, into cancellable I/O and stop generating data when cancellation is observed. I do not assume cancellation automatically rolls back a committed write or undoes a remote side effect.
Streaming allows results to be written progressively instead of collecting an entire response in memory. Its benefit depends on the serializer, middleware, server, and proxy path; returning a lazy sequence does not guarantee every layer flushes each item immediately.
C# example
This minimal example demonstrates propagation rather than a production streaming protocol. In a real stream, I define framing and partial-result behavior so clients know whether the complete result arrived.
Once headers or body bytes have been sent, a later exception cannot reliably become a fresh HTTP 500 Problem Details response. I validate prerequisites before starting, dispose iterators and resources, and design cancellation cleanup. Disconnection is often expected operational behavior, so logging every cancelled stream as an application defect creates misleading noise.
80. How should background work be started from an HTTP request?
I hand off a bounded work item to an explicit background-processing mechanism instead of starting an unobserved task inside the request. A hosted worker can consume an in-process queue for work that may safely be lost during a process failure; durable requirements need a persistent queue or job system.
The request should pass a small immutable description, such as a job identifier and authorized operation parameters. It should not pass HttpContext, a controller instance, or a request-scoped database object into work that outlives the request. The worker creates its own DI scope for scoped services and awaits the work before disposing that scope.
I define what the HTTP response means. A 202 response means the request was accepted for processing, not that processing succeeded. A status resource or another completion mechanism can let the client discover the eventual outcome. Queue capacity also needs a policy: waiting indefinitely or allocating unbounded memory is not acceptable overload handling.
For the implementation, I prefer BackgroundService or a hosted-service abstraction with cancellation and graceful shutdown support. This supplies a lifecycle, not durability or exactly-once execution. Errors must be observed, and duplicate jobs must be handled according to the operation's requirements.
ASP.NET Interview Questions and Answers for Experienced
These answers focus on failure modes, operational evidence, and explicit trust boundaries rather than universal architectures or performance promises.
81. How do you diagnose ThreadPool starvation in an ASP.NET Core application?
I look for blocked request-processing threads rather than assuming high latency means high CPU usage. ThreadPool starvation can produce growing request queues, rising worker-thread counts, and poor throughput while CPU utilization remains relatively low. I correlate runtime counters with request latency, dependency timings, and thread stacks from the affected interval.
Common causes include .Result, .Wait(), synchronous network or database calls, and locks held across slow operations. I replace blocking I/O with genuine asynchronous APIs throughout the call chain. Wrapping a synchronous call in Task.Run inside an endpoint still consumes a worker thread and normally adds scheduling overhead rather than solving the underlying problem.
I also check connection-pool saturation and downstream throttling: both can resemble application slowness and can coexist with starvation. After a targeted change, I repeat the same representative load test and compare tail latency, queue length, throughput, and error rate. Increasing minimum thread counts may change symptoms, but it is not evidence that the blocking design has been corrected.
82. How would you safely stream a large response without exhausting server memory?
I stream incrementally, keep buffering bounded, and propagate cancellation to the data source. Returning an enormous materialized collection can consume memory before the first byte reaches the client; an asynchronous sequence or chunked stream can reduce that pressure. Streaming does not automatically make the database query, serializer, or reverse proxy unbuffered, so I verify the entire path.
This ASP.NET Core endpoint excerpt assumes OpenExportAsync returns an owned, asynchronously readable stream:
C# example
ExportStore is an application service, not a framework type. I authorize the export before writing, limit concurrent exports, and test slow clients. Once response headers or body bytes are sent, I cannot reliably replace the result with a normal JSON error response. For very expensive exports, generating a durable artifact asynchronously may be safer than holding an HTTP request open.
83. How should request-size limits and upload limits be coordinated?
I treat upload limits as a chain of independent gates: edge proxy, IIS or Kestrel, request-body handling, multipart parsing, and application validation. The smallest applicable limit usually wins. Raising only the ASP.NET form limit does not guarantee that a proxy or server will accept the request.
For a self-hosted Kestrel application, this configuration excerpt sets a global body-size limit before the app is built:
C# example
I prefer narrowly scoped larger limits for authorized upload endpoints, with storage quotas, timeouts, concurrency controls, and safe filename handling. Under IIS, the relevant hosting mode and IIS request-filtering settings must also be considered; I do not present a Kestrel-only setting as a universal override.
Large files should normally be streamed into controlled storage instead of fully buffered in memory. I validate actual file content and permitted types, scan where required, and never execute uploaded content. Tests should cover oversized bodies, multipart overhead, slow uploads, aborted connections, and failures during storage writes.
84. How do you propagate request cancellation while enforcing a downstream deadline?
I combine the incoming request's cancellation token with an application-defined downstream deadline, then pass the combined token to operations that support cancellation. A disconnected caller and an exceeded dependency budget are different events, even though both can cancel work.
This C# service-method excerpt assumes an injected HttpClient, an approved Uri target, and the incoming CancellationToken requestAborted:
C# example
The client may have its own timeout, so I document how the budgets interact rather than allowing several independent limits to surprise callers. Cancellation is cooperative: it does not prove that a remote operation stopped or that a database transaction rolled back. I therefore avoid reporting a canceled payment request as definitely uncharged.
I record deadline failures separately from caller disconnects, avoid logging expected cancellation as an unexplained server defect, and reserve enough of the overall request budget for cleanup and returning a response.
85. When can retries turn an ASP.NET operation into a correctness bug?
Retries become a correctness bug when a repeated request can apply the same business effect more than once. A timeout means the caller did not receive a timely result; it does not establish whether the server committed the operation. Blindly retrying a charge, order creation, or message publication can duplicate work.
For retryable writes, I design a durable idempotency contract: a caller-supplied operation key, a binding between that key and the normalized request, and a stored outcome. Concurrent requests with the same key must be coordinated atomically. A process-local dictionary is insufficient across restarts or multiple application instances.
I retry only failures classified as transient, with bounded attempts, jittered delay, and a shared time budget. I honor server retry guidance when appropriate and avoid multiplying retries at every layer. A circuit breaker limits repeated calls to a failing dependency, but does not supply idempotency.
For an ambiguous result, I prefer querying the operation status or reconciling through the idempotency record. The endpoint's contract should explain replay behavior and key retention instead of promising impossible exactly-once network delivery.
86. How should a hosted service create and dispose scoped dependencies?
A hosted service has no automatic HTTP request scope, so I create an explicit scope for each bounded unit of work. I do not inject a scoped database or request service directly into a singleton worker and retain it for the worker's lifetime.
This C# method belongs inside a BackgroundService that receives IServiceScopeFactory scopeFactory. JobProcessor is an application-defined scoped service:
C# example
The scope is disposed only after the awaited work finishes, including asynchronous disposal where supported. Parallel jobs need separate scopes unless their dependencies explicitly support concurrent use. I pass cancellation into the processor and define how failures are logged, retried, or returned to the queue.
I never capture an endpoint's HttpContext, scoped services, or open response stream for later background use. Instead, I copy the small immutable values required by the job, such as an authorized tenant identifier and job ID, and reestablish the necessary security and data boundaries when processing it.
87. When should background work leave the web process for a durable queue?
I move work to a durable queue when losing it during process restart is unacceptable, when execution outlives a practical request deadline, or when its concurrency must be scaled independently. An in-memory channel and a BackgroundService can coordinate local work, but they do not make accepted jobs durable.
For a business-critical operation, the HTTP endpoint should validate and authorize the request, persist acceptance reliably, and return an operation identifier that clients can query. If a database update and message publication must agree, an outbox pattern can record both intent and business state within one local transaction, with a separate dispatcher publishing later.
Workers must tolerate duplicate deliveries and crashes after performing the effect but before acknowledging the message. I use idempotency, bounded retries, dead-letter handling, and operational visibility rather than assuming a queue provides exactly-once business processing.
I also distinguish accepted from completed: a 202 Accepted response is appropriate for an asynchronous contract, but should not claim the requested effect already happened. Queue age, failed deliveries, and completion latency are important service metrics alongside HTTP response times.
88. How do you enforce tenant isolation across an ASP.NET request?
I establish tenant identity from trusted authentication and application routing rules, then enforce it at every resource boundary. A client-supplied tenant header or route value is a requested context, not proof that the caller may access that tenant. I verify membership or delegation before using it.
Tenant identity must flow into data queries, cache keys, object storage paths, background jobs, and authorization decisions. For example, a cache key containing only invoice:123 can expose another tenant's data if identifiers overlap. I include a stable tenant namespace and any relevant authorization-dependent variation.
I keep tenant context scoped to the request or job and avoid mutable singleton state that can leak between concurrent users. Global query filters may help, but they are not a substitute for reviewing raw SQL, administrative paths, and cross-tenant operations. Separate databases or stronger infrastructure boundaries may be required by the isolation model.
Tests should deliberately request another tenant's object identifier, reuse cache entries, and run concurrent tenants. I also audit tenant resolution failures and privileged bypasses without placing sensitive customer content in logs.
89. How do application rate limits behave when an application scales to several instances?
The built-in ASP.NET Core rate limiters maintain limiter state within the application process; their limits are not automatically a single fleet-wide quota. If three replicas each permit the same burst, a caller may obtain more aggregate capacity than a one-instance calculation suggests. I define whether the requirement is per-instance protection, per-user fairness, or a contractual global quota.
This C# registration excerpt creates a small local concurrency limit:
C# example
It requires using Microsoft.AspNetCore.RateLimiting;, app.UseRateLimiter(), and applying .RequireRateLimiting("exports") to the intended endpoint. Endpoint-specific limiting requires routing to have selected the endpoint; identity-based partitions require authentication to run first.
For global quotas I use a coordinated gateway or distributed implementation. I bound partition cardinality and avoid trusting spoofable IP headers. Rate limiting protects application capacity but is not a complete distributed-denial-of-service defense.
90. How would you prevent cache stampedes and stale data across multiple instances?
I separate duplicate regeneration from freshness. Stampede protection limits how many requests recompute a missing value, while invalidation determines when a cached value is no longer acceptable. A lock alone does not make stale data correct.
ASP.NET Core's HybridCache combines local caching with an optional distributed secondary cache and coalesces concurrent work for a key. I still do not assume this is a distributed lock across every replica. I choose bounded expirations, include all relevant tenant and query dimensions in keys, and avoid using a cache as the authoritative ledger for money or permissions.
For cross-instance changes, I account for local copies. Invalidating an entry through one server does not inherently evict every other server's local memory entry. Depending on the consistency requirement, I use short local lifetimes, explicit invalidation messages, versioned keys, or a read path that checks authoritative state.
I also plan for cache failure: controlled fallback, limited regeneration concurrency, and metrics for misses and backend pressure. A cache outage should not silently turn into an unlimited surge against the database.
91. What must be configured for Data Protection in a multi-instance deployment?
Instances that must decrypt the same protected application data need compatible Data Protection configuration, including a shared persistent key ring and the same application discriminator. Authentication cookies and antiforgery values depend on this infrastructure. A key ring stored only inside an ephemeral container may disappear during replacement.
This registration excerpt illustrates a shared directory, not a complete production key-security configuration:
C# example
The directory must actually be durable and available to all relevant instances. I restrict access and configure appropriate key encryption at rest, such as an approved certificate or key-management provider. Selecting an explicit persistence location can disable the default at-rest encryption mechanism, so persistence and protection must be considered separately.
I retain old keys as long as still-valid protected payloads may need them; routine rotation is not the same as deleting history. I also avoid sharing an application name and key ring across unrelated security boundaries merely for convenience, because doing so can broaden which applications can read protected data.
92. Why can users lose authentication or session state after a deployment?
I first distinguish authentication from session. An authentication cookie contains protected authentication information; session generally uses a cookie identifier to locate server-side data. Sharing a session store does not by itself make authentication cookies decryptable, and sharing Data Protection keys does not preserve lost session records.
Deployment failures commonly involve an ephemeral or inaccessible key ring, a changed application discriminator, different cookie settings, process-local session storage, or a new deployment connecting to the wrong distributed store. Load balancing can make these failures appear intermittent because one replica recognizes the state and another does not.
I compare configuration and logs across old and new instances, reproduce with requests routed to different replicas, and inspect cookie attributes without logging cookie values. Domain, path, Secure, SameSite, and expiration all affect whether a browser sends a cookie.
I treat session as disposable convenience state, not the only copy of a shopping order or other critical record. For a rolling deployment, I also verify that old and new versions agree on stored session formats and authentication validation rules. Sticky sessions may hide a misconfiguration temporarily, but they are not a durable replacement for a coherent state strategy.
93. How do you safely process forwarded headers behind a reverse proxy?
I accept forwarded information only from explicitly trusted proxies and process it before middleware that depends on the original scheme, host, or client address. Otherwise, HTTPS redirects, authentication callbacks, and IP-based policies may use the proxy-facing connection rather than the public request.
This ASP.NET Core configuration excerpt trusts one example internal proxy:
C# example
I replace that address with the actual trusted topology and validate how addresses appear to the application. I do not clear all trust restrictions just to make forwarded headers work. In a multi-hop chain, the forwarding limit and trusted hops must reflect the real route.
The edge must remove or overwrite untrusted incoming forwarding headers appropriately. I separately restrict accepted hosts, especially if forwarded host values influence generated links. Configuration must be tested through the actual proxy, not only through direct localhost requests.
94. How do host validation and security headers protect an ASP.NET application?
Host validation prevents the application from treating arbitrary attacker-supplied hostnames as its own when generating links or selecting application context. I configure allowed hosts at the appropriate application and proxy layers and validate forwarded hosts separately when that header is used. Binding a server socket is not the same as validating the HTTP Host value.
An illustrative appsettings.json fragment is:
JSON example
Security headers address different browser behaviors. HSTS tells supporting browsers to prefer HTTPS for the configured scope; a carefully designed Content Security Policy can reduce script-injection impact; framing restrictions can help prevent clickjacking. These are defense-in-depth controls, not replacements for output encoding, authorization, or safe cookie handling.
I deploy policies incrementally, test legitimate scripts and embedded content, and avoid copying a restrictive policy that breaks the application. HSTS rollout deserves care because cached browser policy cannot be immediately undone by a server rollback. I also verify the externally visible response after proxies or CDNs have added, removed, or duplicated headers.
95. How would you prevent server-side request forgery in a URL-fetching endpoint?
I avoid treating an arbitrary user URL as a safe outbound destination. A server-side fetch can otherwise reach internal services, loopback interfaces, cloud metadata endpoints, or unexpected protocols with privileges the caller does not have. I prefer a small allowlist of approved services and resource identifiers over a general-purpose URL proxy.
Where fetching URLs is required, I validate the scheme, hostname, port, redirects, and resolved destination, and enforce network-level egress restrictions. Validation must account for address changes and redirects; checking a string once and then allowing the HTTP client to follow anywhere is not enough. I also bound response size, duration, and concurrency.
Authentication credentials should not be forwarded to user-selected destinations. I use dedicated clients and avoid exposing internal response bodies or detailed connection errors to untrusted callers. The application should not become a network scanner through its error messages.
I test private-address destinations, redirects to restricted hosts, alternative address representations, and oversized responses in a controlled environment. DNS and connection behavior make this a layered security problem, not a single regular-expression check.
96. How should production secrets be delivered and rotated?
I deliver production secrets through an approved secret store or deployment mechanism, grant the application only the permissions it needs, and keep secret values out of source control, images, logs, and diagnostics. ASP.NET Core's configuration abstraction can consume several providers, but the abstraction itself does not encrypt or authorize access to the underlying secret.
User Secrets are a development convenience, not a production vault. Environment variables may be practical deployment inputs, but they can be exposed through process inspection, diagnostic dumps, or misconfigured tooling. I choose the delivery method according to the threat model and operational platform.
Rotation requires more than changing a configuration value. I verify whether the provider reloads, whether the application rereads it, and whether existing connection pools or authentication handlers retain old credentials. A controlled overlap between old and new credentials may be necessary to avoid an outage.
I test revocation and recovery, monitor access to the secret store, and document which service owns rotation. Managed workload identities can reduce stored credentials where supported, but they still require carefully scoped authorization and an incident-response plan.
97. How do you design useful distributed traces without leaking sensitive information?
I trace the request across service boundaries with correlated spans and useful operation names, then combine traces with structured logs and metrics. A slow HTTP request is easier to explain when its database call, downstream request, and queue publication can be examined as one causal path.
I keep span attributes low-cardinality and purposeful: route templates, operation names, dependency identity, and outcome are usually more useful than raw URLs or arbitrary user text. I avoid passwords, authorization headers, cookies, access tokens, and full request bodies. Even query strings and exception messages can contain sensitive data, so redaction and retention policies apply to telemetry as well as application logs.
Incoming trace context is correlation input, not an authenticated identity. I do not authorize a request based on its trace ID, and I avoid letting unbounded externally supplied values become metric labels.
Sampling controls cost but can hide rare failures. I choose a sampling strategy appropriate to incident investigation and measure instrumentation overhead. I also confirm propagation through the actual HTTP clients and messaging libraries instead of assuming that adding a logging provider creates complete distributed traces.
98. How do liveness and readiness checks differ in production?
Liveness answers whether the process should be restarted; readiness answers whether it should currently receive traffic. I keep liveness cheap and avoid making it fail merely because a shared database is briefly unavailable, since restarting every replica can worsen a dependency incident. Readiness may include required initialization and carefully chosen dependency checks.
This C# excerpt separates tagged readiness checks from a process-only liveness endpoint:
C# example
The snippet intentionally has no real dependency checks until they are registered; an empty readiness set is not evidence that the database works. I control access to detailed health output and ensure probes cannot trigger expensive queries or disclose secrets.
Timeouts, probe frequency, failure thresholds, and startup grace periods must match deployment behavior. A single instantaneous success does not establish service health under load, and readiness should be withdrawn before planned draining begins.
99. How do you make shutdown and rolling deployment safe for active requests?
I stop sending new traffic to an instance, allow a bounded period for in-flight work to finish, and coordinate the web host's shutdown budget with the platform's termination grace period. Graceful shutdown is a best-effort opportunity, not a promise that the process will never be killed abruptly.
This host-options excerpt sets an application shutdown timeout:
C# example
It does not configure a load balancer or orchestrator; those must be aligned separately. Hosted services should observe cancellation, stop accepting new jobs, and return unfinished durable work safely to its processing system. I do not rely on StopAsync for the only copy of important data, because a crash may bypass graceful callbacks.
Long-lived connections, streaming responses, and background jobs need explicit policies. I test rolling replacement under representative load, including an instance that exits before its budget expires. Old and new versions must also coexist safely with shared cookies, cache formats, messages, and schemas. Draining traffic alone does not solve compatibility problems between versions.
100. What makes a container image suitable for an ASP.NET production deployment?
A production image should contain only the required published application and runtime, run with the least privileges practical, and have a repeatable build and patching process. I normally build with an SDK image and copy published output into an appropriate runtime image, rather than shipping build tools and source files unnecessarily.
I pin and track the intended runtime family, rebuild for security updates, and verify the image's architecture and operating-system compatibility. Framework-dependent and self-contained deployments have different runtime servicing responsibilities; neither removes the need to patch the application and its dependencies.
Operational configuration belongs outside the immutable image. Persistent uploads, Data Protection keys, and other durable state must not depend on the container's writable layer surviving replacement. I also verify listening ports, non-root permissions, reverse-proxy behavior, health probes, resource limits, and shutdown signals.
The decisive test is the published image under realistic deployment conditions, not only dotnet run on a developer machine. I avoid embedding credentials during image builds and check that build arguments or copied files have not left secrets in an image layer.
101. How would you deploy a database schema change without breaking a rolling release?
I use an expand-and-contract migration so old and new application versions can run against the same database during the rollout. First I add backward-compatible structures, then deploy code that can use them, migrate data safely, and remove obsolete structures only after old readers and writers are gone.
For example, renaming a required column directly can break the previous version immediately. Adding a replacement column, maintaining compatibility while backfilling, and later removing the old column gives the release a controllable transition. The exact plan depends on transaction semantics, table size, and whether old and new writers can coexist without conflicting updates.
I run schema changes through a controlled deployment step with review, backups, observability, and an explicit recovery plan. Letting every web replica race to apply migrations on startup is usually a poor production coordination mechanism.
Rollback also needs analysis: restoring an old application image does not undo a destructive schema change or reverse newly written data. I define compatibility windows and test both forward deployment and rollback against realistic data volumes, rather than treating successful compilation as evidence that the release is reversible.
102. How do you respond when authentication credentials or signing keys are compromised?
I first identify what was compromised and what that credential can authorize. A leaked application secret, a JWT signing key, a refresh token, and a Data Protection key have different scopes and recovery consequences. I contain access, rotate or revoke the affected material, and preserve useful incident evidence without spreading the secret through logs or tickets.
For token-based authentication, removing an issuer's old verification key or applying an explicit revocation strategy may be necessary; merely issuing a new key does not always invalidate existing tokens. For cookie authentication, server-side session validation, security-stamp checks, or forced sign-out policies may be needed depending on the design. Their effectiveness depends on when validation occurs.
I assess whether protected data could have been read or forged and coordinate changes across all replicas and dependent services. Deleting a Data Protection key impulsively can invalidate legitimate payloads and does not reverse data already disclosed.
Finally, I verify containment with controlled tests, monitor suspicious use, and address the exposure path. The operational goal is to remove unauthorized capability while restoring service safely, not simply to make a configuration file look different.
103. What changes when SignalR runs on several application instances?
Several SignalR servers need a scale-out strategy so messages can reach clients connected to different instances. A Redis backplane or Azure SignalR Service can provide that coordination, but they have different operational models. A process-local hub context does not by itself broadcast across an entire fleet.
Connection affinity is also important. ASP.NET Core SignalR normally needs a client's connection-related requests to reach the same server. Documented exceptions include a single server, Azure SignalR Service handling client connections, or clients restricted to WebSockets with negotiation skipped. I do not claim that a Redis backplane automatically removes affinity requirements.
Hub instances are transient, so I keep durable application state outside hub instance fields. I authorize operations and group membership explicitly; knowing a group name is not an authorization check. Reconnection can require the application to reestablish its subscription and state assumptions.
I measure connection counts, message volume, payload size, and slow-client behavior separately from normal request throughput. Deployments should account for dropped connections and reconnection surges, and clients should tolerate disconnection without duplicating business operations.
104. How do interactive server-side Blazor circuits affect scaling and reliability?
Interactive server-side Blazor maintains per-user circuit state on the server, so capacity depends on active and retained disconnected circuits as well as HTTP request volume. Multiple browser tabs can create multiple circuits. This differs from static server rendering and from components running interactively in WebAssembly on the client.
A scoped service in an interactive server circuit is generally scoped to that circuit, not to each UI event as if it were an independent HTTP request. I therefore avoid keeping large data sets, long-lived database units of work, or unnecessary disposable resources in circuit-scoped services.
I plan for disconnects, reconnection, server replacement, and bounded state retention. Important business state belongs in durable storage; the existence of a circuit does not make unsaved edits durable. Any supported circuit-state persistence must be explicitly configured and tested for the state the application actually serializes.
Load balancing and SignalR infrastructure must preserve the required server association for the chosen hosting model. I test memory under realistic concurrent sessions and examine retained objects before calling all memory growth a leak. A graceful user recovery path matters as much as maximizing connection count.
105. How would you investigate a latency regression after a release?
I compare the release against a known baseline using the same workload, traffic mix, and resource limits. I examine latency distributions, especially the tail, alongside throughput, errors, CPU, allocations, garbage collections, ThreadPool queues, and dependency timings. An improved average can hide a serious increase in slow requests.
I use traces and profiles to locate the changed hot path before optimizing. A serialization change, an extra database round trip, a larger response, an exhausted connection pool, or a cache-key change can all explain a web regression without the routing layer being at fault.
I also separate cold-start and warm-state behavior. A release may perform well after warming caches but fail readiness too early or overload dependencies during startup. Load tests should include sustained traffic and realistic concurrency rather than only a short burst on an empty database.
Once a likely cause is isolated, I change one factor, repeat the test, and compare evidence. If production impact exceeds the agreed threshold, I use the rollback or traffic-reduction plan while continuing diagnosis. A speculative micro-optimization is not a substitute for restoring the service.
106. When are object pooling and allocation optimizations worthwhile?
Pooling is worthwhile when profiling shows that repeatedly creating an expensive, reusable object contributes materially to latency or allocation pressure. It is not automatically beneficial for every small object: pool management, retained memory, and reset logic can cost more than ordinary allocation.
I define ownership precisely. A rented object must be returned once, must not remain in use after return, and must be cleared or reset so one request cannot observe another request's data. The pool may be thread-safe while the individual pooled object is not, so I do not share a checked-out mutable instance concurrently without an explicit contract.
For request processing, I first remove unnecessary large buffers and repeated materialization, then consider specialized pooling if measurements justify it. Returning user-controlled data in a reused buffer without clearing the relevant region can become an information-disclosure bug, not merely a performance defect.
I benchmark the published application under representative concurrency and check both memory retention and tail latency. I also test error and cancellation paths to ensure rented resources are returned. A faster happy-path benchmark does not justify a pool that leaks objects under failure.
107. When is Native AOT appropriate for an ASP.NET Core service?
Native AOT is attractive when startup time, deployment size, or memory footprint matters and the application fits the supported feature set. It is not a universal production switch for every ASP.NET Core application. For the .NET 10 baseline, I check the current compatibility matrix rather than assuming MVC, server-side Blazor, dynamic loading, and reflection-heavy dependencies all work unchanged.
An illustrative project-file setting is:
XML example
That setting enables an AOT publishing model; it does not resolve compatibility warnings or make an incompatible application safe. Minimal APIs and supported serialization paths are common candidates, with source-generated metadata where required. MVC is not supported by the documented ASP.NET Core Native AOT feature matrix.
I inspect trimming and AOT warnings, check every dependency, and test the actual published native executable on the target platform. Normal local development may still use JIT execution, so passing dotnet run tests is insufficient. I compare startup and steady-state behavior against a conventional deployment before accepting the operational trade-offs.
108. How do you preserve correctness when optimizing response compression and caching?
I optimize compression and caching only after defining which responses are safe to reuse and which representations differ. A cache key or variation policy must account for every input that changes the response, including tenant, authorization context, query parameters, and negotiated representation where applicable. Accidentally caching one user's personalized response for another user is a correctness and security failure.
Compression changes representation size and CPU cost, not the application's authorization rules. I test content types, already-compressed payloads, proxy behavior, and cache interaction. HTTPS response compression also deserves security analysis when attacker-controlled content is reflected alongside secrets; enabling it indiscriminately can create side-channel risks.
I avoid assuming HTTP response caching and ASP.NET Core output caching have identical policy semantics. I verify the actual middleware's defaults and the effect of any custom policy, especially around authenticated responses and cookies.
The acceptance criteria include correct headers, unchanged body semantics, expected cache-hit behavior, and no cross-user leakage. I measure CPU and bandwidth savings under realistic payloads. If the main delay is a slow database query, compressing the final response may improve transfer cost without solving the latency bottleneck.
109. How do you migrate System.Web HTTP modules and handlers to ASP.NET Core?
I map the legacy module or handler's responsibility to an explicit ASP.NET Core pipeline component, rather than copying its System.Web type into a Core project. HTTP modules subscribe to legacy application lifecycle events; Core middleware wraps the next delegate in an ordered pipeline. A handler that produces a response may become an endpoint, controller action, or terminal middleware.
For example, a simple timing concern can become this middleware excerpt:
C# example
This measures pipeline completion, not successful client receipt of every byte. I place the component according to the behavior it requires and test short-circuiting, exceptions, and response-started cases. I also identify dependencies on HttpContext.Current, session, authentication events, or IIS-only facilities before choosing the replacement. Migration adapters can help selected incremental scenarios, but they do not recreate the entire legacy lifecycle automatically.
110. How would you plan an incremental migration from ASP.NET Framework?
I begin with an inventory of routes, dependencies, authentication, session, static content, deployment constraints, and operational risks. Then I identify a boundary that can move independently, such as a small API or a self-contained user journey. Incremental migration works best when routing can direct selected requests to the new ASP.NET Core application while the legacy application continues serving the rest.
I establish shared contracts before moving traffic: identity, cookies or remote authentication, URL generation, data ownership, error handling, and observability. System.Web adapters may reduce some compatibility work, but each supported behavior must be evaluated; they are not a blanket guarantee that existing Framework code runs unchanged.
I migrate a thin vertical slice, compare behavior with automated tests, and make rollback possible through routing or deployment controls. Shared databases and authentication systems create coupling that must be managed even if the web processes are separate.
The exit criteria should include retiring replaced legacy routes and dependencies, not merely adding a second application forever. I prioritize reducing risk and establishing a repeatable migration path over rewriting every layer simultaneously.
111. Why is migrating Web Forms different from retargeting an ASP.NET Core project?
Web Forms depends on the ASP.NET Framework System.Web programming model, including server controls, postbacks, ViewState, and the page lifecycle. ASP.NET Core does not host Web Forms pages by simply changing the target framework. The user interface and request-handling model require redesign or an incremental replacement strategy.
I first separate reusable business logic from page code-behind and identify where controls implicitly handle state, validation, and events. That behavior must be represented explicitly in the chosen replacement, such as Razor Pages, MVC, or Blazor. Blazor's component model can be familiar to a Web Forms team, but it does not reproduce Web Forms lifecycle or ViewState semantics.
I preserve business outcomes rather than blindly translating control tags. Tests should capture authorization, validation, navigation, accessibility, and data-update behavior, including edge cases previously hidden inside control events.
For a large application, I can keep existing Web Forms routes on the Framework host while replacing selected experiences behind a coordinated entry point. I also review third-party controls and Windows-only dependencies early, because they may determine the migration effort more than the number of pages.
112. Can an ASP.NET Framework application share authentication cookies with ASP.NET Core?
Yes, in specific supported configurations, but ordinary legacy Forms Authentication cookies are not automatically interchangeable with ASP.NET Core authentication cookies. A documented sharing approach uses compatible Microsoft.Owin cookie middleware in the ASP.NET Framework application and coordinated ASP.NET Core Data Protection settings.
Both applications must agree on the relevant cookie name, authentication scheme or authentication type, application discriminator, key storage, and cookie scope. The exact configuration depends on the middleware versions and identity setup. Sharing a browser cookie name alone does not make the protected ticket format compatible.
I treat the shared key ring as a deliberate security boundary: an application allowed to read and write shared authentication tickets can affect the other application's trust. I also test sign-in, sign-out, expiration, renewal, and claims interpretation across both hosts. Sharing identity does not automatically make different user-database schemas or password-validation systems compatible.
For a legacy authentication mechanism that cannot use the sharing approach, remote authentication through supported migration adapters may be more appropriate during transition. That introduces a network dependency and availability considerations, so I choose it consciously rather than describing it as free interoperability.
113. How do you migrate legacy configuration and HttpContext dependencies safely?
I replace implicit global access with explicit configuration and request abstractions, while preserving behavior through tests. Legacy web.config, ConfigurationManager, and HttpContext.Current usage often mixes host settings, business configuration, and per-request state in ways that are difficult to migrate as one unit.
In ASP.NET Core, I bind application settings to validated options and inject them into the services that need them. IIS may still use a deployment web.config; that does not make it the same application-configuration model as legacy ASP.NET. I verify provider precedence and environment overrides so a migrated application does not accidentally use development values in production.
For request-specific values, I pass the needed user, tenant, or correlation information explicitly where possible. IHttpContextAccessor is available for appropriate web-boundary use, but its context can be absent outside a request and should not be captured for background work.
I isolate compatibility adapters behind narrow interfaces and remove them as migration progresses. Tests should cover missing configuration, invalid values, concurrent requests, and work running without an HTTP context. Replacing a namespace while retaining hidden lifetime assumptions is not a complete migration.
114. How do you test security boundaries that unit tests cannot establish?
I test security boundaries through the actual HTTP pipeline, not only by calling controller methods directly. Unit tests are valuable for authorization decisions and validation logic, but they do not establish that middleware ordering, authentication handlers, antiforgery, routing, serialization, or cookie settings work together correctly.
I use integration tests with controlled identities and data to verify unauthenticated, unauthorized, and permitted cases separately. Cross-tenant object access, overposting, missing antiforgery tokens, malformed payloads, and CORS preflight behavior deserve negative tests as well as successful requests. An authentication stub can isolate policy logic, but tests using that stub do not validate the real identity provider integration.
I then test deployment-specific boundaries in an authorized environment: proxy forwarding, HTTPS redirects, cookie attributes, upload limits, and externally visible headers. An in-memory test server cannot fully reproduce IIS, a load balancer, TLS termination, or browser enforcement.
I keep test credentials and data isolated, avoid real customer accounts, and document what each layer proves. Security confidence comes from complementary evidence, not a single green test suite or a checklist of enabled middleware.
115. How do you handle partial failures in a request that spans several systems?
I assume a multi-system operation can fail after some effects have already occurred. A local database transaction cannot automatically roll back an email, an external payment, and another service's committed update. I identify the system of record and define a workflow with explicit states rather than pretending the request is one atomic action.
For reliable message publication alongside a database update, an outbox can persist the intended message in the same local transaction. Consumers should be idempotent because delivery and acknowledgment can fail independently. Where a later step fails, a compensating action may be appropriate, but compensation is business-specific and may not perfectly reverse the original effect.
I expose an operation identifier and a status model when completion is asynchronous or uncertain. The API should distinguish rejected, accepted, completed, and failed states without telling callers to retry blindly.
Timeouts, retries, reconciliation jobs, and audit records are part of the design. I test failures between each pair of steps, including a crash after an external success but before local acknowledgment. The goal is a recoverable business process with observable outcomes, not an unsupported claim of distributed exactly-once execution.
116. How do you prevent long-running requests from overwhelming the application?
I bound admitted work instead of allowing requests to accumulate indefinitely. Useful controls include endpoint concurrency limits, small queues, request and dependency deadlines, input-size limits, and cancellation propagation. I distinguish a rate limit, which controls arrival frequency, from a concurrency limit, which controls how much work remains active.
For an ASP.NET Core request-timeout policy, this illustrative excerpt requires using Microsoft.AspNetCore.Http.Timeouts;:
C# example
A timeout signals cancellation; it cannot forcibly undo already committed external effects or make uncooperative code stop immediately. I pass cancellation into the real work and test the configured behavior outside a debugger, because debugging can affect timeout execution.
For operations that legitimately take minutes, I prefer an accepted-job contract with durable processing and status polling. I measure queue age, rejection rates, and downstream saturation so overload protection remains an intentional service policy rather than a mysterious source of intermittent failures.
117. How should feature flags and configuration changes be controlled in production?
I treat runtime configuration and feature flags as production changes with ownership, validation, auditability, and rollback. A flag can change behavior as significantly as a deployment, especially when it affects authorization, pricing, storage formats, or external calls. It should not bypass the same review expectations merely because no binary changes.
I define safe defaults and what happens if the configuration provider is unavailable. Reloadable options do not mean every consumer observes a change at the same instant; some components capture values when created, and several replicas may update at different times. For a multi-step operation, I may snapshot the relevant decision so the request does not switch behavior midway through execution.
I use gradual rollout with health metrics and explicit stop conditions. Flags intended for temporary migration should have an owner and removal plan so combinations do not grow into an untestable permanent state space.
I also avoid putting secrets into a broadly accessible flag system. Tests cover disabled, enabled, missing, and invalid configuration states, plus compatibility between old and new code during a rolling release. A rollback must account for any irreversible side effects already produced by the enabled behavior.
118. How do you design safe error responses after response streaming has begun?
Once the response has started, I cannot safely replace it with a new status code and a conventional error document. Headers may already be committed, and the client may have consumed part of the body. I therefore perform authorization, input validation, and avoidable setup failures before starting a stream.
If a failure occurs later, I follow the protocol's contract. A connection may need to end, or a streaming format may support an explicit error record that the client knows how to interpret. I do not append arbitrary JSON to a partially written file or return a successful-looking truncated export without a way to detect incompleteness.
HttpResponse.HasStarted helps identify whether headers are committed, but checking it does not create a recovery mechanism. Exception middleware cannot generally rewrite a response that has already begun. I log a correlation identifier and the failure without exposing internal details to the client.
For business-critical exports, I may generate and validate a durable artifact before serving it, with length or integrity information where appropriate. Tests must interrupt generation and client consumption at different points, not only verify a successful small response.
119. What would you inspect when an application works locally but fails behind IIS?
I separate process startup from request-routing failures. First I check whether the published application starts under the deployment identity, whether the required runtime or hosting bundle is installed for the deployment model, and whether architecture and filesystem permissions match. Then I inspect IIS and ASP.NET Core Module diagnostics for the specific failure.
I verify the chosen hosting model rather than assuming every IIS application is a reverse proxy to Kestrel. In-process hosting runs the application inside the IIS worker process; out-of-process hosting forwards to a separate application process. That distinction affects troubleshooting and configuration.
If startup succeeds, I compare environment variables, connection strings, working directories, certificates, request limits, authentication settings, and the deployment web.config. I also check URL base paths and forwarded scheme behavior. A developer's interactive account may have permissions that the application pool identity does not.
Temporary diagnostic logging should be narrowly scoped, access-controlled, and removed after investigation. I do not enable a detailed developer exception page for public production users. Finally, I reproduce with the actual published output and deployment settings, because local development hosting is not equivalent evidence.
120. How would you design a reliable ASP.NET service for bursty, business-critical traffic?
I start with the business contract: which requests need immediate completion, which can be accepted asynchronously, what data must remain correct, and what latency and availability targets matter. Then I design admission control, persistence, dependency budgets, and recovery around those requirements rather than adding scale-out as the only answer.
The HTTP path should validate and authorize early, avoid blocking I/O, enforce bounded work, and use clear timeout and idempotency semantics. Expensive durable work can move to a queue with observable operation status. Shared authentication keys, appropriate state storage, and tenant-safe cache keys allow replicas to serve traffic consistently without relying on accidental process affinity.
I instrument requests, dependencies, queues, and business outcomes; define readiness and draining behavior; and test bursts, slow dependencies, cache outages, restarts, and partial failures. Capacity planning includes downstream limits, not just web-server CPU.
Finally, I make releases reversible where possible through backward-compatible schemas and contracts, and keep an incident playbook with clear operational thresholds. Reliability is demonstrated by measured behavior and successful recovery exercises. A collection of ASP.NET middleware registrations alone does not prove that the complete service can meet its commitments.
ASP.NET Documentation and Further Reading
The modern baseline is ASP.NET Core 10 on .NET 10 LTS, a supported release at review time. Legacy ASP.NET on .NET Framework, System.Web, Web Forms, and MVC 5 are explicitly identified and are not compatible substitutes for ASP.NET Core APIs. Examples are scoped excerpts, not a complete production application. Verify current servicing, operating-system support, package versions, and hosting configuration before deployment.
These 15 curated primary references cover the framework baseline, implementation, security, testing, operations, and legacy boundaries. They are a focused reading list rather than every page consulted. Documentation review and static checks do not establish runtime execution or production readiness.
Similar Posts
