ASP.NET Core Practice Test

โ–ถ

The asp.net core web api framework has become the dominant choice for building high-performance HTTP services on the .NET platform, powering everything from small internal microservices to global enterprise APIs that handle millions of requests per second. Built on top of Kestrel, the cross-platform web server, it combines the productivity of C# with raw throughput that often outpaces frameworks written in Go or Node.js in benchmark comparisons. Whether you are migrating from legacy WCF services or starting a greenfield project, understanding this framework is essential for modern .NET developers in 2026.

What sets ASP.NET Core Web API apart from its predecessors is its modular pipeline. Middleware components are wired together in a precise order, each one inspecting or modifying the HTTP context before passing it down the chain. This design replaces the monolithic HttpModule and HttpHandler model of classic ASP.NET, giving you fine-grained control over authentication, logging, exception handling, and response compression. The result is an architecture where you pay only for the features you actually use, keeping memory footprints small and startup times under a second.

Controllers remain the workhorse abstraction for routing HTTP verbs to C# methods, but minimal APIs introduced in .NET 6 offer a leaner alternative for simple endpoints. The framework supports both styles side by side, letting teams choose between attribute-based MVC controllers for complex domains and lambda-based minimal endpoints for lightweight services. Either approach produces the same compiled output, taking advantage of source generators, ahead-of-time compilation, and the trimmed publishing options that ship with the .NET SDK.

Dependency injection is no longer an optional library you bolt on after the fact. It is woven into the framework itself, with the built-in service container handling controller activation, scoped database contexts, singleton caches, and transient utility services. This first-class DI support encourages testable design, because every dependency a controller needs arrives through its constructor. Mock implementations slot in cleanly during unit tests, and integration tests can swap entire service registrations to point at in-memory databases or fake message brokers.

Model binding and validation work together to translate raw JSON or form data into strongly typed C# objects, applying data annotations or FluentValidation rules before the action method runs. When validation fails, the framework returns a structured ProblemDetails response that complies with RFC 7807, giving clients machine-readable error information out of the box. This consistency across APIs reduces the friction of consuming services and improves the developer experience for both producers and consumers of your endpoints.

Performance has been a relentless focus of the team behind the framework, and each release brings measurable improvements to JSON serialization, header parsing, and connection management. System.Text.Json now handles polymorphic types and source generation, while the new HTTP/3 support over QUIC reduces tail latency for mobile clients. For teams comparing platforms, you can review the broader ecosystem in the ASP.NET Core Overview: Complete Framework Guide for context on how Web API fits into the larger Microsoft web stack.

This guide walks through the entire lifecycle of building a production-ready service, from project scaffolding and routing conventions to securing endpoints with JWT tokens, documenting them with OpenAPI, and deploying to containers or serverless platforms. Whether you are preparing for a technical interview, studying for certification, or shipping your first microservice next week, the patterns and pitfalls covered here will save you hours of trial and error and help you build APIs that scale gracefully under real-world traffic.

ASP.NET Core Web API by the Numbers

โšก
7M+
Requests per second
๐Ÿ“Š
38%
Faster than .NET 7
๐Ÿ’ป
3.2M+
Developers worldwide
๐ŸŒ
4.5K
NuGet packages
๐Ÿ’ฐ
$112K
Median US salary
Try Free ASP.NET Core Web API Practice Questions

Project Setup and Architecture Patterns

โšก Minimal API Template

Best for microservices and small, focused endpoints. Uses top-level statements in Program.cs, lambda-based route handlers, and zero ceremony for getting a JSON endpoint running in under twenty lines of code.

๐Ÿ“‹ MVC Controller Template

Ideal for larger domains with shared filters, complex routing, and team conventions. Provides attribute routing, model binding, action filters, and the [ApiController] attribute that enables automatic 400 responses on validation errors.

๐Ÿ† Clean Architecture Layout

Separates Domain, Application, Infrastructure, and Presentation layers into distinct projects. Encourages testability, dependency inversion, and the ability to swap persistence layers without touching business rules or HTTP-facing code.

๐ŸŽฏ Vertical Slice Architecture

Organizes code by feature rather than technical concern. Each slice owns its request handler, validator, database query, and response DTO, often using MediatR to decouple controllers from business logic and keep features highly cohesive.

Routing sits at the heart of every Web API, mapping incoming HTTP requests to the C# methods that handle them. ASP.NET Core supports two routing flavors: attribute routing, where you decorate controller actions with [HttpGet("products/{id:int}")] style attributes, and conventional routing, where patterns are registered centrally in Program.cs. For Web APIs, attribute routing is the strong recommendation because URL structure tends to be tightly coupled to specific resources, and seeing the route directly above the method keeps intent obvious during code review.

Route constraints help eliminate entire classes of bugs by rejecting malformed requests before they reach your action. The :int constraint ensures id values are integers, while :guid, :datetime, and :regex give you finer control. Custom constraints can validate complex patterns like ISBN numbers or product SKUs by implementing IRouteConstraint. When a constraint fails, the framework returns a 404 automatically, sparing you from defensive null checks and parsing exceptions deeper in the request pipeline.

Controller design benefits enormously from the [ApiController] attribute, which opts your class into a bundle of convenient behaviors: automatic model state validation, problem details responses, binding source inference, and attribute routing requirements. Without this attribute, you find yourself writing if (!ModelState.IsValid) return BadRequest() at the top of every action, cluttering your code with boilerplate that adds no business value. With it, validation failures are intercepted automatically and returned as RFC 7807 compliant payloads.

HTTP verb selection deserves more thought than developers typically give it. GET should be idempotent and safe, never mutating server state. POST creates new resources and is not idempotent. PUT replaces a resource entirely and is idempotent on retry. PATCH applies a partial update, often using JSON Patch or JSON Merge Patch semantics. DELETE removes a resource and should also be idempotent in practice, returning the same result whether the resource existed or not. Honoring these semantics makes your API predictable for clients and proxy caches.

Action results in ASP.NET Core are typed return values that translate cleanly to HTTP responses. Returning Ok(product) produces a 200 with the serialized product body, while NotFound() produces a 404, and CreatedAtAction(...) returns a 201 with a Location header pointing at the newly created resource. The ActionResult<T> pattern lets you mix typed success responses with non-success results in a single method signature, giving Swagger and OpenAPI generators enough metadata to produce accurate documentation.

API versioning becomes critical the moment your service has external consumers. The Asp.Versioning.Mvc package supports versioning through URL segments, query strings, headers, or media types. Most teams settle on URL segment versioning (/api/v1/products) because it is easy to test in a browser and explicit in logs. Combine it with the deprecation header and a clear sunset policy so clients know when to migrate. For deeper request handling patterns, see C# ASP.NET Core Read Request Body: Complete Technical Tutorial Guide.

Finally, controllers should remain thin. They orchestrate, but they do not contain business logic. Push validation rules into your domain layer, persistence into repositories or DbContext extensions, and cross-cutting concerns into filters or middleware. A controller method that fits on one screen and reads like a recipe (validate, dispatch, return) is easier to maintain, easier to test, and easier to evolve as requirements change over the multi-year life of a production service.

ASP.NET Core Authentication & Authorization
Test your knowledge of JWT, cookies, identity, and policy-based authorization with 25 scenario-based questions.
ASP.NET Core Authentication & Authorization 2
Deepen your understanding with role claims, OAuth flows, and custom requirements in this follow-up quiz.

Security: Authentication and Authorization in ASP.NET Core Web API

๐Ÿ“‹ JWT Bearer Tokens

JSON Web Tokens are the dominant choice for securing modern Web APIs because they are stateless, self-contained, and easy to verify across distributed services. The Microsoft.AspNetCore.Authentication.JwtBearer package adds middleware that validates signature, issuer, audience, lifetime, and any custom claims on every request without contacting a central session store.

Configure validation parameters carefully. Always require HTTPS in production, use asymmetric keys (RS256 or ES256) when tokens are issued by an identity provider, and set short expiration times paired with refresh tokens. Never embed sensitive data in a JWT payload because anyone holding the token can decode it. Use claims for identity, but store sensitive attributes server-side keyed by the user identifier.

๐Ÿ“‹ Policy-Based Authorization

Roles work for simple scenarios but break down when business rules become more nuanced. Policy-based authorization lets you compose requirements such as MinimumAgeRequirement, SameOrganizationRequirement, or HasActiveSubscriptionRequirement, each evaluated by a dedicated AuthorizationHandler. Policies are registered once in Program.cs and applied to controllers or actions with [Authorize(Policy = "PolicyName")].

This approach scales because new requirements compose with existing ones, and unit tests can assert behavior of individual handlers in isolation. For resource-based authorization, where the decision depends on the specific entity being accessed, inject IAuthorizationService and call AuthorizeAsync inside your action with the resource and policy name as parameters.

๐Ÿ“‹ API Key and OAuth Flows

API keys still have a place for server-to-server integrations where bearer tokens are overkill. Implement a custom AuthenticationHandler that reads a header like X-API-Key, hashes it, and compares against a database of registered consumers. Rate limit each key and rotate them on a schedule, treating leaked keys with the same urgency as leaked passwords.

For user-facing applications, the authorization code flow with PKCE is the modern standard, replacing the deprecated implicit flow. ASP.NET Core integrates cleanly with identity providers like Auth0, Okta, Azure AD, and Duende IdentityServer. The framework handles the redirect dance, token exchange, and refresh logic, leaving your application code free to focus on business behavior rather than protocol mechanics.

ASP.NET Core Web API: Pros and Cons

Pros

  • Cross-platform support on Windows, Linux, and macOS with identical APIs and tooling
  • Exceptional performance, regularly placing in the top tier of TechEmpower benchmarks
  • First-class dependency injection container built into the framework with no third-party libraries required
  • Strong typing through C# with nullable reference types catching common bugs at compile time
  • Rich ecosystem with NuGet packages for every conceivable integration and infrastructure need
  • Excellent tooling in Visual Studio, JetBrains Rider, and Visual Studio Code with full IntelliSense
  • Native OpenAPI generation through Swashbuckle or the built-in Microsoft.AspNetCore.OpenApi package

Cons

  • Steeper learning curve than Express or Flask for developers new to the .NET ecosystem
  • Cold start times on serverless platforms remain slower than Node.js without AOT compilation
  • Larger published artifacts compared to Go binaries or Python wheels for minimal services
  • Some legacy patterns from full .NET Framework still surface in older tutorials and confuse newcomers
  • Container images are larger than Alpine-based alternatives unless trimmed and AOT-compiled
  • Microsoft documentation can lag behind preview features, leaving gaps for early adopters
ASP.NET Core Authentication & Authorization 3
Tackle advanced scenarios involving multi-tenant security, claim transformations, and custom token providers.
ASP.NET Core Configuration & Environments
Master appsettings layering, environment variables, user secrets, and IOptions pattern with realistic questions.

Production-Ready ASP.NET Core Web API Checklist

Enable HTTPS redirection and HSTS for all production endpoints
Configure CORS policies explicitly rather than allowing all origins
Add health check endpoints for liveness and readiness probes
Implement structured logging with Serilog or the built-in ILogger
Set up centralized exception handling middleware returning ProblemDetails
Enforce request size limits to prevent memory exhaustion attacks
Add rate limiting with the built-in RateLimiter middleware
Generate OpenAPI documents and publish them with a Swagger UI in non-production environments
Use response compression for JSON payloads larger than a few kilobytes
Configure connection pooling for Entity Framework Core or your data access library
Wire up Application Insights, OpenTelemetry, or another observability platform
Run security scanning on dependencies and container images during CI
Async All the Way Down

Mixing synchronous and asynchronous code in your request pipeline is the single most common cause of thread pool starvation in production. Once you commit to async at the controller level, every database call, HTTP request, file read, and queue operation in the chain must also be async. A single .Result or .Wait() can deadlock under load and silently double your response times.

Performance tuning for ASP.NET Core Web API begins with measurement, not guesswork. Tools like BenchmarkDotNet, dotnet-counters, dotnet-trace, and PerfView reveal exactly where your service spends its time and memory. Without baseline measurements, optimization efforts often target the wrong bottlenecks, delivering negligible gains while making code harder to read. Profile a representative workload, identify the top three time sinks, and address them in order before moving on to micro-optimizations.

JSON serialization is frequently the largest CPU cost in a busy API. System.Text.Json, the default serializer since .NET Core 3.0, outperforms Newtonsoft.Json by significant margins, especially with source generation enabled. Decorating your DTOs with [JsonSerializable] and registering a JsonSerializerContext lets the SDK generate fast, reflection-free serialization code at compile time, reducing both startup time and steady-state CPU usage. The technique pairs well with ahead-of-time compilation for truly minimal cold starts.

Caching is the second largest performance lever. Output caching, introduced in .NET 7 and refined in subsequent releases, lets you cache entire responses keyed by route, query string, and custom variables. For finer control, IMemoryCache stores arbitrary objects in process memory, while IDistributedCache uses Redis or SQL Server to share cache state across multiple instances. Always set explicit expiration windows and consider cache stampede protection with techniques like coalescing concurrent misses.

Database access typically dominates request latency for data-heavy APIs. Entity Framework Core has matured into a competitive ORM, but it still benefits from careful query design. Use AsNoTracking for read-only queries, project to DTOs with Select to avoid loading unnecessary columns, and watch out for N+1 patterns when iterating collections. The new ExecuteUpdateAsync and ExecuteDeleteAsync methods let you perform bulk operations without materializing entities, dramatically improving performance for batch jobs.

Connection pooling deserves explicit attention. Both EF Core and raw ADO.NET share pools per connection string, but misconfiguration can exhaust the pool under load and trigger timeouts. Set Max Pool Size in your connection string based on actual database capacity, and monitor open connections through diagnostic counters. For HTTP clients, always use IHttpClientFactory rather than newing up HttpClient directly, since the factory manages socket reuse and DNS refresh transparently.

Native ahead-of-time compilation, available in .NET 8 and matured in .NET 9, produces self-contained binaries that start in milliseconds and run with no JIT overhead. The tradeoff is that some runtime features require source generators or compile-time alternatives, including reflection-heavy code. AOT is ideal for serverless deployments where cold start latency directly impacts user experience and infrastructure costs, especially when combined with container images measured in tens of megabytes.

Finally, profile in production with care. APM tools like Application Insights, Datadog, New Relic, and OpenTelemetry-based collectors capture distributed traces, exception rates, and dependency latencies with low overhead. Sample appropriately to control costs, but never sample errors or slow requests. The patterns that surface from production telemetry frequently differ from synthetic load tests, revealing edge cases like degraded DNS resolution or noisy neighbor virtual machines that no microbenchmark would expose.

Testing strategy for ASP.NET Core Web API services should span three layers: unit tests for business logic, integration tests for controller and middleware behavior, and end-to-end tests for critical user journeys. Unit tests run in milliseconds and execute the largest volume, isolating pure C# logic with mocked dependencies. Integration tests use the WebApplicationFactory class to spin up the full request pipeline in process, validating routing, model binding, filters, and middleware exactly as production would execute them.

WebApplicationFactory deserves special attention because it bridges the gap between unit and end-to-end testing. It starts your application in memory with the real DI container, real configuration, and real middleware, but lets you substitute specific services like the database context or external HTTP clients. Tests issue requests through an HttpClient that talks directly to the in-memory server, avoiding network overhead while exercising the entire framework stack. A well-written integration suite catches regressions that unit tests cannot.

Contract testing tools like Pact or Microsoft's OpenAPI-driven Contract testing extension verify that producers and consumers agree on request and response schemas. When a producer changes a property name without updating the contract, the tests fail before the change reaches staging. This shift-left approach to compatibility issues is invaluable in microservice architectures where teams deploy independently and integration drift can break dozens of consumers overnight.

Containerization with Docker has become the default deployment model. The dotnet publish command supports container generation out of the box since .NET 7, removing the need for hand-written Dockerfiles in many cases. For a leaner image, base your build on the chiseled Ubuntu or distroless images Microsoft publishes, then layer your compiled application on top. Combined with AOT compilation, final images can shrink to 30 MB while still running on standard Kubernetes clusters without modification.

Configuration management deserves more careful thought than it usually receives. ASP.NET Core layers configuration sources, starting with appsettings.json, overlaying environment-specific files, then environment variables, command line arguments, and user secrets in development. This hierarchy makes it trivial to ship a single binary that behaves differently in dev, staging, and production based purely on environment variables. Avoid baking secrets into images. Use Key Vault, AWS Secrets Manager, or HashiCorp Vault and inject values at runtime.

Deployment platforms vary widely. Azure App Service offers the smoothest experience for teams already in the Microsoft ecosystem, providing zero-downtime deployment slots, automatic certificate management, and tight integration with Application Insights. AWS supports .NET on Elastic Beanstalk, ECS, and Lambda. Kubernetes works equally well on any cloud or on-premises cluster. For runtime troubleshooting, consult ASP.NET Core Errors: HTTP 500.30 and Common Startup Failures Explained when boot fails after deployment.

CI/CD pipelines should run unit tests, integration tests, security scans, and container builds on every commit, gated by quality checks before merge. GitHub Actions and Azure DevOps both have first-class .NET support with caching for NuGet packages and parallel test execution. Adopt trunk-based development with feature flags rather than long-lived branches, since the latter cause painful merge conflicts and obscure ownership. A small, frequent release cadence outperforms infrequent big-bang deployments on almost every measure of stability and recovery time.

Master ASP.NET Core Configuration with Practice Tests

Practical tips from teams running ASP.NET Core Web APIs at scale repeatedly emphasize boring fundamentals over clever tricks. Keep your dependencies current. The .NET team ships security patches frequently, and falling more than one major version behind means missing performance gains and accumulating technical debt that compounds with every preview release. Schedule a quarterly upgrade ritual and treat it as non-negotiable, even when business priorities push back. Stagnant frameworks become liabilities faster than most engineers realize.

Document your API the moment you create endpoints, not as a final polish step before launch. OpenAPI annotations on controllers and DTOs flow directly into generated documentation, client SDKs, and contract tests. Consumers should be able to read your Swagger UI and understand request shapes, response codes, and authentication requirements without asking a single question. Treat the OpenAPI document as a first-class deliverable that lives alongside your source code in version control, evolving with every pull request.

Embrace structured logging from day one. Plain string logs are nearly useless when troubleshooting distributed systems. Serilog with its message templates and enrichers makes it natural to log structured data: user IDs, request correlation IDs, tenant identifiers, and operation names. Ship logs to Elasticsearch, Seq, Splunk, or any centralized aggregator and query them by attribute rather than free text. The difference in mean time to resolution between text logs and structured logs is often measured in hours.

Correlate every request across services with distributed tracing. The W3C trace context propagates automatically through HttpClient and the major queue clients, building a single trace from initial user click through every downstream service. Combined with OpenTelemetry instrumentation, you get a view of latency hot spots that no monolithic log file can produce. When a complaint comes in about a slow page, a single trace ID lets you see exactly where time went, end to end.

Build resilience into clients, not just servers. The Microsoft.Extensions.Http.Resilience package provides retries with exponential backoff, circuit breakers, timeouts, and bulkhead isolation. Wire these patterns into every HttpClient that calls another service, because transient failures in distributed systems are routine. A service that gracefully retries a 503 once or twice per minute will absorb failures that would otherwise cascade into customer-visible outages. Resilience is a property of the entire system, not any single tier.

Plan for observability before you need it. Once production catches fire, the time to add metrics is gone. Emit business metrics like orders per minute, cart abandonment, and API key usage alongside technical metrics like CPU and memory. Dashboards built on Prometheus or Application Insights tell the story of your system's behavior under real load, and alerts based on rate-of-change rather than absolute thresholds catch problems earlier. The investment pays for itself within the first incident it helps you resolve.

Finally, write code your future colleagues can read. The cleverest LINQ chain or the most elegant generic constraint can become someone else's nightmare six months from now. Lean toward explicit, named methods over deeply nested expressions. Favor composition over inheritance. Keep functions short, names meaningful, and comments focused on why rather than what. The codebases that survive multiple team turnovers are the ones that respect the next developer who has to debug a problem at 2 AM.

ASP.NET Core Configuration & Environments 2
Continue your configuration mastery with advanced scenarios involving Key Vault, hot reload, and named options.
ASP.NET Core Configuration & Environments 3
Tackle the toughest configuration challenges: validation, post-configuration, and multi-tenant settings strategies.

Asp Net Core Questions and Answers

What is the difference between ASP.NET Core Web API and ASP.NET Core MVC?

ASP.NET Core Web API and MVC are unified under a single framework since ASP.NET Core 1.0. The same ControllerBase class powers both. The practical difference is intent: Web API controllers inherit from ControllerBase and return data (typically JSON), while MVC controllers inherit from Controller and can return Razor views. The [ApiController] attribute opts your class into API-specific conveniences like automatic validation responses and ProblemDetails error formats.

Should I use Minimal APIs or Controllers in ASP.NET Core?

Use Minimal APIs for small services with few endpoints, simple routing, and minimal filters. They reduce ceremony and produce identical performance to controllers. Use Controllers for larger domains with shared filters, complex routing, model binders, and team conventions. Both can coexist in the same project, and Minimal APIs now support most controller features including OpenAPI metadata, route groups, and parameter binding from custom sources, narrowing the gap considerably.

How do I version an ASP.NET Core Web API correctly?

Install the Asp.Versioning.Mvc and Asp.Versioning.Mvc.ApiExplorer packages. Configure versioning in Program.cs and choose a strategy: URL segment (/api/v1/), query string, header, or media type. URL segment is most popular because it is explicit and testable. Apply [ApiVersion("1.0")] to controllers, mark old versions deprecated, and publish a sunset header. Generate separate OpenAPI documents per version for clear consumer documentation.

What is the best way to handle exceptions globally?

Use the UseExceptionHandler middleware combined with IExceptionHandler implementations introduced in .NET 8. Each handler inspects the exception and decides whether to handle it, returning a ProblemDetails response that complies with RFC 7807. Order handlers from most specific to most general, ending with a fallback that catches anything unhandled. Always log full exception details server-side and return generic messages to clients to avoid leaking sensitive implementation details.

How does dependency injection work in ASP.NET Core Web API?

The framework ships with a built-in service container registered in Program.cs through builder.Services. Services are added with three lifetimes: Singleton (one instance for the application lifetime), Scoped (one per HTTP request), and Transient (a new instance every time it is requested). Controllers receive dependencies through constructor injection automatically. The container supports keyed services, factory registrations, and decorator patterns, making third-party DI libraries optional for most scenarios.

What is the difference between IActionResult and ActionResult<T>?

IActionResult is the non-generic interface that lets a controller return any HTTP response without specifying a body type. ActionResult<T> is a generic wrapper that allows returning either a typed value or an action result. Use ActionResult<T> when your action sometimes returns a typed value and sometimes a different status code, because it gives OpenAPI generators enough metadata to document both the success body shape and the alternative status codes.

How do I secure an ASP.NET Core Web API with JWT?

Install Microsoft.AspNetCore.Authentication.JwtBearer. Configure builder.Services.AddAuthentication().AddJwtBearer with TokenValidationParameters specifying issuer, audience, signing keys, and lifetime validation. Add UseAuthentication before UseAuthorization in the middleware pipeline. Decorate controllers with [Authorize]. Issue tokens through an identity provider like Duende IdentityServer, Azure AD, Auth0, or your own login endpoint. Always use HTTPS and short token lifetimes paired with refresh tokens for security.

What is the OpenAPI specification and how do I generate it?

OpenAPI is a JSON or YAML document that describes your API's endpoints, request and response shapes, authentication, and metadata. ASP.NET Core supports generation through Swashbuckle.AspNetCore (most popular) or the newer Microsoft.AspNetCore.OpenApi package introduced in .NET 9. Annotate actions with XML doc comments, response type attributes, and tags to enrich the document. Serve Swagger UI in non-production environments so consumers can explore endpoints interactively.

How do I implement rate limiting in ASP.NET Core Web API?

Use the built-in RateLimiter middleware introduced in .NET 7. Configure limiters in Program.cs with strategies like fixed window, sliding window, token bucket, or concurrency limits. Apply policies globally or per endpoint with [EnableRateLimiting("PolicyName")]. Partition limiters by user identifier, IP address, or API key so abusive clients do not affect legitimate traffic. Return 429 Too Many Requests with a Retry-After header when limits are exceeded.

What are health checks and why does my API need them?

Health checks are endpoints that report whether your API is alive and ready to serve traffic. Liveness checks confirm the process is running. Readiness checks verify downstream dependencies like databases and message queues. Kubernetes, Azure App Service, and load balancers poll these endpoints to decide whether to route traffic or restart instances. Install Microsoft.Extensions.Diagnostics.HealthChecks, register checks for your dependencies, and map them to /healthz/live and /healthz/ready routes.
โ–ถ Start Quiz