ASP.NET Web API Interview Cheat Sheet
Top 50 interview questions with concise answers. Print this page or save as PDF for offline study.
1. What is ASP.NET Core Web API and how does it differ from MVC?
ASP.NET Core Web API builds HTTP APIs (REST) without the View layer. MVC includes Views for server-side HTML rendering. Web API returns JSON/XML data consumed by front-end apps, mobile clients, or other services. Both share the same middleware pipeline, DI, and routing infrastructure. Web API controllers inherit from ControllerBase (no View support); MVC from Controller.
2. Explain the request pipeline in ASP.NET Core Web API.
The request pipeline is a chain of middleware components. Each middleware can inspect and modify the request, call the next middleware, and modify the response on the way back. Order matters - defined in Program.cs. For Web API: UseRouting -> UseAuthentication -> UseAuthorization -> MapControllers. Request flows forward through middleware, response flows backward.
3. What are Controllers and how are they structured?
Controllers group related API endpoints. Each controller class inherits ControllerBase and is decorated with [ApiController] and [Route("api/[controller]")]. Action methods handle specific HTTP verbs ([HttpGet], [HttpPost], [HttpPut], [HttpDelete]). [ApiController] enables automatic model validation, attribute routing, and better error responses. Return IActionResult or ActionResult for typed responses.
4. How does Attribute Routing work in Web API?
Attribute routing maps HTTP requests to controller actions using attributes directly on methods. [Route("api/users/{id}")] defines the URL template. [HttpGet("{id}")] matches GET requests. Route parameters ({id}) bind to action parameters. Constraints: {id:int}, {name:alpha}. More explicit than convention-based routing - each endpoint's URL is visible right on the action method.
5. Explain model binding and validation.
Model binding automatically maps HTTP request data (route params, query string, body, headers, form) to action method parameters. [FromBody] binds JSON request body. [FromQuery] binds query string. [FromRoute] binds route parameters. [FromHeader] binds headers. [ApiController] enables automatic validation - if ModelState is invalid, it returns 400 Bad Request without needing to check manually.
6. What are Action Results and why are they important?
Action Results are objects returned from controller actions that determine the HTTP response. Common: Ok(data) -> 200, Created(uri, data) -> 201, BadRequest(errors) -> 400, Unauthorized() -> 401, NotFound() -> 404, NoContent() -> 204. Use ActionResult as return type for both typed returns and IActionResult alternatives. They abstract HTTP status codes from business logic.
7. What is Content Negotiation in Web API?
Content Negotiation lets the client specify what format it wants (JSON, XML) via the Accept header. The server checks available output formatters and picks the best match. ASP.NET Core includes JSON formatter by default. Add XML: services.AddControllers().AddXmlSerializerFormatters(). If no match: returns default format (406 Not Acceptable if configured strictly).
8. How are dependency injection and services handled in Web API?
ASP.NET Core has built-in DI. Register services in Program.cs: builder.Services.AddScoped(). Controllers declare dependencies as constructor parameters - DI injects them automatically. This enables loose coupling, testability (inject mocks), and lifetime management. Never create services with new inside controllers - always inject via constructor.
9. Explain the difference between Scoped, Singleton, and Transient lifetimes.
Transient: new instance created every time it's requested (stateless, lightweight services). Scoped: one instance per HTTP request - shared within that request (EF Core DbContext). Singleton: one instance for the app lifetime - shared across all requests (config, caches). Never inject Scoped into Singleton - creates a captive dependency (the Scoped object lives longer than intended).
10. How do you handle exceptions in Web API?
Exception handling options: try-catch in action methods, global exception handling middleware (IMiddleware or app.UseExceptionHandler()), or ProblemDetails pattern (RFC 7807). Exception filters catch exceptions from action execution. UseExceptionHandler is the recommended global approach - returns consistent error responses. Never expose stack traces in production.
11. How does ASP.NET Core handle JSON serialization?
ASP.NET Core uses System.Text.Json by default (fast, low-allocation). Configure globally in AddControllers(options => ...). Options: JsonNamingPolicy.CamelCase, ReferenceHandler.Preserve for circular refs, custom converters for types like DateOnly. Newtonsoft.Json (AddNewtonsoftJson()) is optional for backwards compatibility. Use [JsonPropertyName("name")] to customize JSON property names per class.
12. What are Action Filters and when do you use them?
Action filters run code before/after action execution. Use for: logging, input validation, performance timing, caching headers. Implement IActionFilter (sync) or IAsyncActionFilter (async). OnActionExecuting runs before the action, OnActionExecuted runs after. Apply as [attribute] on controller or action, or register globally in MVC options. Different from middleware: filters have access to action context (controller, action name, parameters).
13. Explain the purpose of FromBody, FromQuery, and FromRoute.
[FromBody]: deserializes JSON/XML request body into a parameter - use for POST/PUT with complex data. [FromQuery]: reads from URL query string (?name=Alice). [FromRoute]: reads from URL path template ({id}). [FromHeader]: reads from HTTP header. [FromForm]: reads from form data. Without these, [ApiController] uses smart inference: complex types default to [FromBody], primitives to [FromQuery].
14. How do you implement versioning in Web API?
API versioning strategies: URL path (/api/v1/users), query string (?api-version=1), headers (API-Version: 1). Use the Microsoft.AspNetCore.Mvc.Versioning package. [ApiVersion("1.0")] on controllers. Run multiple versions side by side. Deprecate old versions with [ApiVersion("1.0", Deprecated = true)]. URL path versioning is the most visible and REST-compatible approach.
15. What is CORS and why is it important in Web API?
CORS (Cross-Origin Resource Sharing) restricts which origins can make requests to your API from browsers. Configure in Program.cs: builder.Services.AddCors() and app.UseCors(). Define policies: AllowAnyOrigin(), WithOrigins("https://yourapp.com"), AllowAnyMethod(), AllowAnyHeader(). Apply globally or per controller/action. Allow only specific trusted origins - avoid AllowAnyOrigin in production.
16. How do you secure a Web API?
Secure Web API: use JWT or OAuth2 for authentication. Enforce HTTPS. Apply [Authorize] to protect endpoints. Use CORS to restrict origins. Validate all inputs (model validation). Rate limit to prevent abuse. Return minimal error details (no stack traces). Keep dependencies updated. Use HTTPS-only cookies if using cookies. Apply principle of least privilege for service accounts and API keys.
17. Explain the difference between synchronous and asynchronous controllers.
Synchronous controllers block a thread for the duration of I/O operations. Async controllers: use async Task return type and await I/O calls (DbContext, HttpClient). The thread is released during await, allowing the server to handle other requests. Under load, async prevents thread exhaustion. Rule: always use async for any controller that does I/O (database, HTTP, file).
18. What is Response Caching in Web API?
Response caching stores HTTP responses to serve repeated requests faster. [ResponseCache] attribute sets Cache-Control headers telling clients/proxies how long to cache. UseResponseCaching() middleware caches on the server side. VaryByQueryKeys caches different versions per query parameter. Not suitable for user-specific or highly dynamic responses. Use distributed caching (Redis) for server-side caching of database results.
19. How do you test a Web API effectively?
Web API testing: unit tests with xUnit/NUnit + Moq (mock services, test action logic in isolation). Integration tests with WebApplicationFactory - spins up the real app with an in-memory or test database, makes HTTP calls with HttpClient. Test all HTTP verbs, status codes, error cases. Use TestServer for lightweight integration tests without a real HTTP server.
20. Why is Swagger/OpenAPI important in Web API?
Swagger (OpenAPI) auto-generates interactive API documentation from your code. Developers can explore endpoints, see request/response schemas, and test calls directly in the browser. Add with Swashbuckle.AspNetCore. Integrate XML comments for detailed descriptions. Add JWT auth support in Swagger UI. Essential for API-first development and enabling frontend/mobile teams to understand the API contract.
21. What is Middleware in ASP.NET Core Web API?
Middleware is a component in the request pipeline that processes HTTP requests and responses. Each middleware calls next() to pass control forward. Custom middleware: implement IMiddleware or write a request delegate (async (context, next) => {...}). Register with app.UseMiddleware(). Order matters - authentication must run before authorization, error handling must be first.
22. How does the UseRouting and UseEndpoints middleware work?
UseRouting() matches the incoming request URL to an endpoint (stores the match in HttpContext). UseEndpoints() (or MapControllers()) executes the matched endpoint. Middleware between UseRouting and UseEndpoints has access to the matched endpoint info (e.g., UseAuthorization can read [Authorize] attributes on the matched action). Without UseRouting first, authorization can't know which endpoint was matched.
23. How do you create custom middleware?
Custom middleware: write an async method with (HttpContext context, RequestDelegate next) parameters, or implement IMiddleware. Call await next(context) to continue pipeline. Modify request before, response after. Register: app.Use(async (context, next) => {...}) or app.UseMiddleware(). Use for: request logging, adding headers, timing, IP filtering - anything that applies to all requests.
24. Explain Filters in ASP.NET Core Web API.
ASP.NET Core filters run at specific points around action execution. Types: Authorization (first - can short-circuit), Resource (before/after model binding), Action (before/after action runs), Exception (handles unhandled exceptions), Result (before/after result executes). Filters are MVC-specific (have access to action context) while middleware applies to all requests including static files.
25. How is model validation applied automatically?
[ApiController] attribute enables automatic model validation: if ModelState.IsValid is false after model binding, it automatically returns 400 Bad Request with validation error details - no manual check needed. Define validation with DataAnnotations ([Required], [MaxLength], [EmailAddress]) or FluentValidation. Customize the 400 response using InvalidModelStateResponseFactory in MVC options.
26. How do you implement global exception handling using middleware?
Global exception handling with middleware: app.UseExceptionHandler(app => app.Run(async context => { var error = context.Features.Get(); log error; await context.Response.WriteAsJsonAsync(new ProblemDetails {...}); })). In .NET 8: use IExceptionHandler interface and app.AddExceptionHandler(). Returns RFC 7807 ProblemDetails format. Always handle exceptions globally - don't rely on developers to try-catch everywhere.
27. Explain the difference between synchronous and asynchronous middleware.
Synchronous middleware blocks the thread for its duration. Async middleware uses async/await to release the thread during I/O waits. Use async middleware for any I/O in the pipeline (logging to a service, reading from Redis, DB calls). Synchronous middleware in async pipelines can cause thread pool starvation under load. Always write middleware as async unless it has no I/O.
28. How can you implement logging in Web API?
ASP.NET Core logging: inject ILogger via DI. Log with logger.LogInformation(), LogWarning(), LogError(). Configure providers in appsettings.json (Console, File via Serilog/NLog, Application Insights). Use structured logging: logger.LogInformation("User {UserId} logged in", userId) - preserves the userId as a queryable field. Serilog or NLog for file sinks and centralized logging.
29. What is diagnostic middleware and why is it important?
Diagnostic middleware provides insights into request processing. UseDeveloperExceptionPage() shows detailed error info in development. UseStatusCodePages() adds custom responses for 404/500. Built-in health check middleware (UseHealthChecks) reports app status. Custom diagnostics middleware can log request/response details, timing, and add correlation IDs to all requests for distributed tracing.
30. How do you handle CORS in a Web API?
Handle CORS: add services (AddCors), define a named policy with allowed origins, methods, headers. Apply policy globally (app.UseCors("MyPolicy")), per controller ([EnableCors("MyPolicy")]), or per action. For credentials (cookies, auth headers): call AllowCredentials() and specify exact origins (not wildcard). UseResponseCaching middleware needs CORS configured before it.
31. How do you restrict content types in requests?
Restrict content types with a custom action filter or resource filter that checks request Content-Type header. Reject requests with unsupported content types (return 415 Unsupported Media Type). [Consumes("application/json")] attribute on action methods declares what content types are accepted and restricts routing to matching requests. Prevents processing unexpected content formats.
32. How do you inspect and log request/response bodies safely?
Log request/response bodies: use middleware that buffers the request body (EnableBuffering() to make it re-readable), reads and logs it, then resets the stream position. For responses: replace the original response body stream with a MemoryStream, let the pipeline write to it, log it, then copy to the original. Be careful: don't log sensitive data (passwords, tokens). Limit logged body size.
33. What is the difference between endpoint routing and legacy MVC routing?
Endpoint routing (current): UseRouting() matches routes, UseEndpoints() executes them. Middleware in between can inspect the matched endpoint. Supports any endpoint type (controllers, Razor pages, gRPC, health checks). Legacy MVC routing: route matching done inside MVC itself, no separation. Endpoint routing is more flexible, allows authorization to read endpoint metadata before execution.
34. How do you implement API versioning?
API versioning with Microsoft.AspNetCore.Mvc.Versioning: AddApiVersioning() in services. [ApiVersion("1.0")] on controllers. URL path: v1/api/users, query string: ?api-version=1, header: api-version: 1. MapToApiVersion(1.0) maps actions to specific versions. Return supported versions in API-Supported-Versions response header. Deprecate old versions gracefully with documentation.
35. How do you configure JSON serialization globally?
Configure JSON globally: builder.Services.AddControllers().AddJsonOptions(opts => { opts.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; opts.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; opts.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter()); }). Add custom converters for complex types. Override per controller with [JsonOptions].
36. How do you handle API response formatting?
Handle API response formatting: return typed ActionResult for auto-serialization. Use ProducesResponseType attributes for Swagger documentation. Set Content-Type in responses. Implement custom output formatters for non-JSON formats. Use ProblemDetails for error responses (RFC 7807 standard). Consistent response structure: always include status, data, and error fields for predictable client handling.
37. How do you throttle requests in ASP.NET Core Web API?
Rate limiting in ASP.NET Core 7+: built-in RateLimiter middleware. Policies: FixedWindowLimiter, SlidingWindowLimiter, TokenBucketLimiter, ConcurrencyLimiter. Add with AddRateLimiter(). Apply globally or per endpoint with [EnableRateLimiting("policy")]. Returns 429 Too Many Requests with Retry-After header. Identify clients by IP, user ID, or API key for per-client limits.
38. What are dependency injection best practices in Web API?
DI best practices: program to interfaces (inject IRepository, not Repository). Use Scoped for request-scoped state (DbContext, user context). Use Singleton for shared stateless services (HttpClient via IHttpClientFactory, configuration). Never capture Scoped in Singleton. Use AddHttpClient() for HttpClient (manages connection pooling). Avoid service locator pattern (don't inject IServiceProvider to call GetService manually).
39. How do you monitor Web API performance in production?
Monitor Web API: Application Insights (auto-collects requests, dependencies, exceptions, performance counters). Health checks endpoint (/health) for load balancer probes. Prometheus + Grafana for metrics. Structured logging with Serilog to ELK/Seq. Key metrics: request rate, error rate, latency (p95/p99), DB query time, thread pool size. Set up alerts on error rate spikes and latency degradation.
40. How do you implement authentication in Web API?
JWT authentication: client sends Authorization: Bearer header. AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer() validates the JWT signature, issuer, audience, and expiration. Claims from the token populate HttpContext.User. Apply [Authorize] to require authentication. Use [Authorize(Roles = "Admin")] for role-based access. Always use HTTPS to protect tokens in transit.
41. How does JWT authentication work?
JWT workflow: client authenticates (username/password) and receives a signed JWT from the auth server. JWT contains claims (user ID, roles, expiry) in a Base64-encoded payload. The server signs with a secret/private key. On subsequent requests, client sends the JWT. Server validates signature and expiry without hitting the database. Short-lived access tokens (15 min) + refresh tokens for renewal.
42. How do you implement role-based authorization?
Role-based authorization: add roles as claims in the JWT or use ASP.NET Core Identity roles. [Authorize(Roles = "Admin,Manager")] restricts to users with those roles. Multiple roles in one attribute = OR logic. Stack multiple [Authorize] attributes for AND logic. Check roles in code: User.IsInRole("Admin"). Roles are a simple but coarse-grained authorization mechanism.
43. What is claims-based authorization?
Claims-based authorization: JWT/Identity tokens carry claims (key-value pairs about the user). [Authorize(Policy = "MinimumAge")] uses a policy that evaluates claims. Define policies: builder.Services.AddAuthorization(opts => opts.AddPolicy("MinimumAge", p => p.RequireClaim("age"))). More flexible than roles - claims can carry any attribute (department, subscription level, specific permissions).
44. How do you create custom authorization policies?
Custom authorization policies: AddPolicy("CanEdit", policy => policy.Requirements.Add(new CanEditRequirement())). Implement IAuthorizationHandler: check the requirement against user claims and resource. Register handler with DI. Apply with [Authorize(Policy = "CanEdit")]. Resource-based authorization: pass the resource to IAuthorizationService.AuthorizeAsync(User, resource, "CanEdit").
45. What is the difference between authentication and authorization?
Authentication: verifies who the user is (reads JWT/cookie, validates signature, extracts claims into HttpContext.User). Must happen before authorization. Authorization: decides what the authenticated user can do (reads claims, evaluates policies, checks [Authorize] attributes). These are separate concerns and separate middleware. A user can be authenticated (valid token) but not authorized (wrong role).
46. How do you secure sensitive API endpoints?
Secure sensitive endpoints: require authentication ([Authorize]), enforce HTTPS, apply role/policy authorization, rate limit login and sensitive operations, log all access attempts (with user ID and IP), validate all inputs, use parameterized queries for DB access, and return minimal error details. For extra-sensitive data: add IP allowlisting, require MFA, and audit every access.
47. How do you handle token expiration in JWT?
JWT expiration handling: issue short-lived access tokens (15-60 min) and longer-lived refresh tokens. When access token expires, client uses refresh token to get a new access token from the auth endpoint. Validate refresh tokens against a store (Redis/DB) to allow revocation. Rotate refresh tokens on use (detect replay attacks). Use sliding expiration: refresh token TTL resets on each use.
48. How do you protect against CSRF attacks in APIs?
CSRF attacks trick authenticated users into making unwanted requests. For stateless JWT APIs (Authorization header): CSRF is not a concern (browsers don't auto-send custom headers). For cookie-based auth: use antiforgery tokens (ValidateAntiforgeryToken), SameSite=Strict/Lax cookie attribute, or custom request header validation. Most REST APIs using Bearer tokens don't need CSRF protection.
49. How do you secure API keys in Web API?
Secure API keys: store in environment variables or Azure Key Vault (never in code or config files). Pass as Authorization header or custom header (not in URL - it gets logged). Hash and store API keys server-side (don't store plaintext). Rotate keys regularly. Scope keys to minimum required permissions. Rate limit per API key. Log all API key usage for auditing.
50. What is OAuth2 and how is it used with Web API?
OAuth2 is an authorization framework. Web API acts as a Resource Server that accepts OAuth2 access tokens. An Authorization Server (IdentityServer, Azure AD, Auth0) issues tokens. Clients get tokens by presenting credentials (client credentials flow for machine-to-machine, authorization code flow for users). The API validates tokens using the auth server's public keys (JWKS endpoint).