Skip to main content

Mid ASP.NET Web API Interview Questions

Curated Mid-level ASP.NET Web API interview questions for developers targeting mid positions. 38 questions available.

Last updated:

ASP.NET Web API Interview Questions & Answers

Skip to Questions

Welcome to our comprehensive collection of ASP.NET Web API interview questions and answers. This page contains expertly curated interview questions covering all aspects of ASP.NET Web API, from fundamental concepts to advanced topics. Whether you're preparing for an entry-level position or a senior role, you'll find questions tailored to your experience level.

Our ASP.NET Web API interview questions are designed to help you:

  • Understand core concepts and best practices in ASP.NET Web API
  • Prepare for technical interviews at all experience levels
  • Master both theoretical knowledge and practical application
  • Build confidence for your next ASP.NET Web API interview

Each question includes detailed answers and explanations to help you understand not just what the answer is, but why it's correct. We cover topics ranging from basic ASP.NET Web API concepts to advanced scenarios that you might encounter in senior-level interviews.

Use the filters below to find questions by difficulty level (Entry, Junior, Mid, Senior, Expert) or focus specifically on code challenges. Each question is carefully crafted to reflect real-world interview scenarios you'll encounter at top tech companies, startups, and MNCs.

Questions

38 questions
Q1:

How do you implement API versioning effectively?

Mid

Answer

API versioning ensures backward compatibility and predictable updates.

Common approaches:

  • URL versioning: /api/v1/resource
  • Query versioning: ?api-version=1.0
  • Header versioning: api-version: 1.0

Use the Microsoft.AspNetCore.Mvc.Versioning package and decorate controllers with [ApiVersion("1.0")].

Quick Summary: API versioning effectively: choose strategy (URL path is most common: /api/v1/). Use Microsoft.AspNetCore.Mvc.Versioning package. [ApiVersion("1.0")] on controllers, [MapToApiVersion] on actions. Maintain old versions as long as clients use them. Track version usage via analytics. Deprecate with advance notice and a sunset date. Never break an existing version without a major version bump.
Q2:

How do you implement compression in Web API?

Mid

Answer

Enable compression using:

services.AddResponseCompression()

Supports Gzip and Brotli to reduce payload size and improve performance.

Quick Summary: Response compression reduces payload size. UseResponseCompression() middleware compresses responses. Add providers: AddResponseCompression(opts => opts.Providers.Add()). Client signals support via Accept-Encoding: gzip header. Compress text responses (JSON, HTML) but not already-compressed files. Set minimum size threshold to avoid compressing tiny responses (overhead > benefit).
Q3:

How do you handle exceptions globally?

Mid

Answer

Global exception handling centralizes error responses.

Use:

  • UseExceptionHandler() middleware
  • Custom exception-handling middleware
  • Standardized error payloads and logging
Quick Summary: Global exception handling: use app.UseExceptionHandler() middleware. Catches unhandled exceptions, logs them, and returns a structured error response. .NET 8: implement IExceptionHandler interface, register with AddExceptionHandler(). Return ProblemDetails (RFC 7807) for consistent error format across all endpoints. Never expose exception details (stack trace, internal messages) to API clients in production.
Q4:

What are best practices for structured error responses?

Mid

Answer

Include:

  • statusCode
  • message
  • errors (validation details)
  • traceId

Avoid exposing internal details for security.

Quick Summary: Structured error responses: use RFC 7807 ProblemDetails format: {type, title, status, detail, instance}. Return consistent error shapes for all error types. Include a unique error code that clients can reference. For validation errors: include field-level details. Configure in AddProblemDetails() (ASP.NET Core 7+). Clients can parse errors reliably rather than parsing varied error message strings.
Q5:

How do you implement rate limiting / throttling?

Mid

Answer

Use libraries like AspNetCoreRateLimit to:

  • Limit requests per IP or client
  • Protect server from abuse
  • Apply global or endpoint-specific rules
Quick Summary: Rate limiting in .NET 7+: AddRateLimiter() in services. Policies: FixedWindowLimiter (X requests per window), SlidingWindowLimiter (smoother counting), TokenBucketLimiter (allows bursts), ConcurrencyLimiter (max simultaneous requests). Identify clients by IP or user ID with a partitioning key. Returns 429 Too Many Requests with Retry-After. Apply per-route or globally.
Q6:

How do you handle large payloads efficiently?

Mid

Answer

Use streaming techniques:

  • IFormFile for uploads
  • Avoid full in-memory buffering
  • Enable compression where needed
Quick Summary: Handle large payloads: increase RequestSizeLimitAttribute per endpoint or globally in Kestrel options. Use streaming: read request body as a stream instead of loading entirely into memory (IFormFile for file uploads, JsonSerializer.DeserializeAsync with Stream). Enable request body buffering only when needed. For very large uploads, use direct-to-blob streaming (Azure Blob, S3) rather than proxying through the API.
Q7:

How do you implement conditional requests?

Mid

Answer

Use:

  • ETag
  • Last-Modified
  • If-None-Match or If-Modified-Since

Returns 304 Not Modified when content is unchanged.

Quick Summary: Conditional requests reduce bandwidth. Server sends ETag (hash of content) and Last-Modified in responses. Client caches and sends If-None-Match/If-Modified-Since on next request. Server compares: if unchanged, return 304 Not Modified (no body). Implement in middleware or action filters. Useful for read-heavy endpoints where data changes infrequently. Reduces server load and network traffic for unchanged resources.
Q8:

How do you prevent over-fetching in Web APIs?

Mid

Answer

Use field selection / projection such as:

?fields=name,email

Implement DTOs to avoid returning unnecessary properties.

Quick Summary: Prevent over-fetching: return only requested fields. Use GraphQL for client-specified field selection. Or implement sparse fieldsets: GET /api/users?fields=id,name,email. Design DTOs per use case (list DTO with fewer fields, detail DTO with more). Avoid returning entire entity graphs when only a few fields are needed. Reduces payload size and serialization cost.
Q9:

How do you implement HATEOAS in Web API?

Mid

Answer

HATEOAS enriches responses with navigational links.

Example:

Order response may include:

  • self link
  • update link
  • cancel link
Quick Summary: HATEOAS (Hypermedia As The Engine Of Application State): responses include links to related actions. Example: GET /api/orders/123 returns the order plus links: {self: /api/orders/123, cancel: /api/orders/123/cancel, items: /api/orders/123/items}. Clients discover available actions from responses rather than hardcoding URLs. Adds flexibility but increases response size and client complexity. Rarely implemented in practice.
Q10:

How do you implement distributed caching?

Mid

Answer

Use:

  • Redis
  • SQL Server distributed cache
  • Memcached

Useful for multi-instance API deployments.

Quick Summary: Distributed caching with Redis: IDistributedCache interface injected, backed by Redis. cache.GetStringAsync(key), SetStringAsync(key, value, options). Set TTL with DistributedCacheEntryOptions. Use for: session state, frequently read but rarely changing data, pre-computed responses. IMemoryCache is per-server (lost on restart, not shared in scaled apps). Redis is shared across all instances.
Q11:

How do you secure sensitive data in responses?

Mid

Answer

Avoid sending PII or sensitive fields.

Use DTOs, masking, or encryption.

Always enforce HTTPS.

Quick Summary: Secure sensitive data in responses: never return passwords or hashed passwords. Exclude PII fields not needed by the client (use [JsonIgnore] or response DTOs). Mask sensitive fields (show last 4 digits of card). Encrypt sensitive data stored in DB. Use HTTPS to protect in-transit data. Implement field-level encryption for highly sensitive fields. Audit access to sensitive data endpoints.
Q12:

How do you handle versioning breaking changes?

Mid

Answer

Best practices:

  • Maintain old versions
  • Introduce new versioned endpoints
  • Document migration paths
Quick Summary: Handle versioning breaking changes: increment major version for breaking changes (/api/v2/). Maintain v1 until clients migrate. Track which clients use which version via analytics. Communicate deprecation schedule early. Use API changelog and migration guide. Breaking changes include: removing fields, changing field types, changing required/optional status, removing endpoints. Adding optional fields is non-breaking.
Q13:

How do you implement pagination?

Mid

Answer

Use Skip and Take (LINQ).

Include:

  • page
  • pageSize
  • totalCount
  • totalPages
Quick Summary: Pagination: return page of results with metadata. Offset-based: GET /api/users?page=2&pageSize=20 - simple but slow for large offsets. Cursor-based (keyset): GET /api/users?after=lastId&limit=20 - efficient for large datasets. Response: {data: [...], nextCursor: "...", totalCount: N}. Use EF Core Skip/Take for offset or WHERE id > cursor for keyset. Return Link headers for page navigation.
Q14:

How do you implement filtering and sorting?

Mid

Answer

Expose query parameters for filtering and sorting:

  • ?status=active
  • ?sort=name_desc

Combine with pagination for scalable responses.

Quick Summary: Filtering and sorting: accept filter params in query string: GET /api/orders?status=active&minAmount=100. Sorting: GET /api/users?sort=lastName&dir=asc. Use a filter model class bound with [FromQuery]. Map to EF Core LINQ: .Where(o => o.Status == filter.Status). For complex filtering, use specification pattern or OData. Sanitize sort field names against a whitelist to prevent injection.
Q15:

How do you monitor API performance in production?

Mid

Answer

Use monitoring tools:

  • Application Insights
  • Prometheus + Grafana
  • Serilog

Track duration, throughput, failure rates, response sizes.

Quick Summary: Monitor API performance: track request rate, error rate, and latency (p50/p95/p99) with Application Insights, Prometheus, or Datadog. Add custom metrics (business KPIs, cache hit rate). Health checks endpoint for infrastructure probes. Distributed tracing (OpenTelemetry) to trace requests across services. Alert on: error rate > 1%, p99 latency > threshold, health check failures.
Q16:

How do you implement API health checks?

Mid

Answer

Use built-in health checks:

services.AddHealthChecks();

Expose /health endpoint to monitor DB, disk, and external services.

Quick Summary: Health checks: AddHealthChecks() in services, MapHealthChecks("/health") to expose. Add checks for DB (AddDbContextCheck()), Redis (AddRedis()), and custom checks. Separate /health/live (is the process running) from /health/ready (is it ready for traffic). Kubernetes uses liveness and readiness probes against these endpoints. Return 200 (healthy), 503 (unhealthy).
Q17:

How do you maintain backward compatibility while refactoring endpoints?

Mid

Answer

Keep older versions active.

Deprecate API versions gradually.

Provide documentation for migration.

Quick Summary: Backward compatibility while refactoring: don't rename, remove, or change required/optional on existing endpoint parameters. Add new endpoints rather than modifying old ones. Use API versioning to introduce breaking changes as v2. Mark old behavior as deprecated with deprecation warnings. Write contract tests that validate the existing API contract is preserved after each refactor.
Q18:

How do you implement API documentation effectively?

Mid

Answer

Use Swagger / OpenAPI:

  • Interactive documentation
  • Multiple version support
  • Authentication integration
  • Schema validation
Quick Summary: API documentation with Swagger: install Swashbuckle.AspNetCore. AddSwaggerGen() in services, UseSwagger() and UseSwaggerUI() in middleware. Add XML comments: tags and [ProducesResponseType] attributes generate rich docs. Configure JWT auth in Swagger UI for testing protected endpoints. Generate OpenAPI spec for client code generation. Keep docs in sync - use contract-first approach.
Q19:

How do you integrate Entity Framework Core with Web API?

Mid

Answer

To integrate EF Core with Web API:

  • Install EF Core packages such as Microsoft.EntityFrameworkCore and Microsoft.EntityFrameworkCore.SqlServer.
  • Register your DbContext in Program.cs using AddDbContext.
  • Inject DbContext via constructor injection in controllers or services.
  • Use migrations and LINQ for strongly-typed database access.
Quick Summary: EF Core integration: install EF Core package + DB provider (EF Core SqlServer, Npgsql). Add DbContext via AddDbContext() with connection string from config. Use Scoped lifetime (one instance per request). Access entities via DbSet. Use async methods (ToListAsync, FirstOrDefaultAsync) for all DB operations. Apply migrations to manage schema changes.
Q20:

What is the difference between DbContext and DbSet?

Mid

Answer

DbContext manages the database connection, querying, saving, and tracking of entities.

DbSet<T> represents a table and allows querying and CRUD operations for that entity type.

Quick Summary: DbContext is the unit of work and gateway to the database. It manages the connection and tracks entity changes. DbSet represents a table - use it to query (LINQ) and modify (Add, Remove) entities. One DbContext instance per request (Scoped). Multiple DbSet properties (one per entity type) in a DbContext. SaveChangesAsync() persists all tracked changes to the DB in one transaction.
Q21:

How do you implement DTOs and why?

Mid

Answer

DTOs decouple API contracts from internal entity models.

Benefits:

  • Prevents over-posting
  • Improves security
  • Helps with API versioning
  • Simplifies maintenance

Mapping can be done manually or using AutoMapper.

Quick Summary: DTOs (Data Transfer Objects) separate the API contract from domain entities. Input DTOs: define exactly what clients should send (prevent over-posting). Output DTOs: define exactly what clients receive (prevent leaking sensitive fields). DTOs are stable even when domain entities change. Use AutoMapper or manual mapping to convert. Never expose domain entities directly as API request/response models.
Q22:

What is the Repository Pattern and why use it?

Mid

Answer

The Repository Pattern abstracts the data access layer.

It provides:

  • Clean separation of concerns
  • Mockable interfaces for testing
  • Consistent CRUD interface
Quick Summary: Repository pattern abstracts data access behind an interface. IUserRepository defines operations (FindByIdAsync, GetAll, Add). Implementation uses EF Core. Controllers inject IUserRepository - decoupled from EF Core. Benefits: testability (mock the repository), swappable data source, centralized query logic. Debate: EF Core DbContext already implements repository pattern - adding another layer may be unnecessary overhead.
Q23:

How do you implement Unit of Work pattern?

Mid

Answer

The Unit of Work pattern groups operations under one transaction.

DbContext naturally acts as a Unit of Work; calling SaveChangesAsync commits all changes atomically.

Quick Summary: Unit of Work coordinates multiple repository operations in a single transaction. IUnitOfWork holds references to repositories and has SaveChangesAsync(). All operations complete before committing. If any fails, all roll back. In practice with EF Core: DbContext IS the Unit of Work (tracks all changes across DbSets and commits them together). Adding a separate UoW layer over EF Core is often redundant.
Q24:

How do you handle transactions in EF Core?

Mid

Answer

Use explicit transactions via:

await context.Database.BeginTransactionAsync();

Wrap operations in try/catch to commit or rollback.

Quick Summary: EF Core transactions: by default, SaveChangesAsync() wraps all changes in a transaction. Explicit transactions: using var tx = await context.Database.BeginTransactionAsync(); try { ... await context.SaveChangesAsync(); await tx.CommitAsync(); } catch { await tx.RollbackAsync(); }. Use explicit transactions when you need multiple SaveChanges or mix EF Core with raw SQL in one transaction.
Q25:

How do you implement asynchronous queries in EF Core?

Mid

Answer

Use async LINQ extensions:

  • ToListAsync()
  • FirstOrDefaultAsync()
  • SingleOrDefaultAsync()

Improves scalability by freeing threads during database I/O.

Quick Summary: Async EF Core queries: always use async methods - ToListAsync(), FirstOrDefaultAsync(), SingleOrDefaultAsync(), CountAsync(), AnyAsync(), SaveChangesAsync(). These return Task and release the thread during DB I/O. Mixing sync and async EF Core operations in the same DbContext instance can cause issues. Never use .Result or .Wait() on EF Core async operations.
Q26:

How do you implement filtering and sorting with EF Core?

Mid

Answer

Use LINQ:

  • Where() for filtering
  • OrderBy()/OrderByDescending() for sorting
  • Skip()/Take() for pagination
Quick Summary: Filtering with EF Core: apply Where() predicates that translate to SQL WHERE clauses. Sorting: OrderBy(x => x.Name).ThenBy(x => x.Id). Dynamic sorting: use reflection or a library like System.Linq.Dynamic.Core to build expressions from string sort fields. Always validate sort field names against allowed values to prevent injection. Apply pagination after filtering and sorting.
Q27:

How do you prevent over-posting when updating entities?

Mid

Answer

Use DTOs and map only allowed fields.

Avoid binding client input directly to entity models.

Quick Summary: Prevent over-posting in EF Core updates: never use context.Update(entity) with a fully entity-mapped request body. Instead: load the existing entity from DB, map only allowed fields from the DTO to the entity, call SaveChangesAsync(). Only the mapped fields change. Or use separate update DTOs with [BindNever] on sensitive properties. Entity.Entry(entity).Property(x => x.Name).IsModified = true for granular updates.
Q28:

How do you implement soft deletes?

Mid

Answer

Add an IsDeleted flag and filter queries:

.Where(x => !x.IsDeleted)

Useful for audits and data recovery.

Quick Summary: Soft deletes mark records as deleted without removing them. Add IsDeleted bool and DeletedAt timestamp columns. Override SaveChangesAsync to auto-set these on delete. Use EF Core global query filters: modelBuilder.Entity().HasQueryFilter(x => !x.IsDeleted) - automatically excludes deleted records from all queries. Disable with IgnoreQueryFilters() when needed. Useful for audit trails and data recovery.
Q29:

How do you implement optimistic concurrency in EF Core?

Mid

Answer

Use a RowVersion (timestamp) column.

EF detects conflicts and throws DbUpdateConcurrencyException.

Quick Summary: Optimistic concurrency: add a rowversion/timestamp column. EF Core adds it to UPDATE WHERE clause - if another process changed the row, the WHERE fails (0 rows affected) and throws DbUpdateConcurrencyException. Handle: catch the exception, reload the entity (fresh values), show conflict to user or auto-merge based on rules. [Timestamp] attribute on byte[] property enables rowversion-based concurrency.
Q30:

How do you implement eager, lazy, and explicit loading?

Mid

Answer

Eager: Include()

Lazy: Navigation property auto-loading (requires proxies)

Explicit: context.Entry(entity).Collection(...).Load()

Quick Summary: Eager loading: Include() loads related entities in one query (JOIN). Good when you always need the related data. Lazy loading: related entities load when first accessed (requires proxy). Risk of N+1 queries - avoid in Web API. Explicit loading: context.Entry(entity).Collection(x => x.Orders).LoadAsync() - load on demand. Choose eager for predictable queries, explicit for conditional loading, avoid lazy in APIs.
Q31:

How do you handle many-to-many relationships in EF Core?

Mid

Answer

EF Core 5+ supports many-to-many with:

HasMany().WithMany()

Junction table is auto-created unless custom entity is needed.

Quick Summary: Many-to-many in EF Core 5+: define navigation collections on both sides, EF Core creates the join table automatically (no explicit join entity needed). modelBuilder.Entity().HasMany(s => s.Courses).WithMany(c => c.Students). For a join entity with extra fields (enrollment date): use explicit join entity with two foreign keys. Query: student.Courses or context.StudentCourses.Where(...).
Q32:

How do you handle transactions across multiple DbContext instances?

Mid

Answer

Use TransactionScope for distributed transactions.

Or share the same database connection among contexts.

Quick Summary: Multiple DbContext transactions: use TransactionScope (distributed) or manually share a connection. Better approach: use a single DbContext that spans all operations. If you must use multiple DbContexts, create a shared IDbContextTransaction: var outer = await context1.Database.BeginTransactionAsync(); await context2.Database.UseTransactionAsync(outer.GetDbTransaction()). Rarely needed - redesign to use one DbContext.
Q33:

How do you execute raw SQL queries in EF Core?

Mid

Answer

Use:

  • FromSqlRaw() for queries
  • ExecuteSqlRaw() for commands

Always use parameters to prevent SQL injection.

Quick Summary: Raw SQL in EF Core: context.Database.ExecuteSqlRawAsync("DELETE FROM Logs WHERE CreatedAt < {0}", cutoff) for commands. context.Users.FromSqlRaw("SELECT * FROM Users WHERE Name = {0}", name) for queries (still tracked). Use parameterized queries (not string concatenation) to prevent SQL injection. Return mapped entities with FromSqlRaw, anonymous types with Dapper alongside EF Core.
Q34:

How do you implement caching for database queries?

Mid

Answer

Use:

  • IMemoryCache for local cache
  • Redis for distributed cache

Cache expensive queries and invalidate on updates.

Quick Summary: Cache DB queries: IMemoryCache for single-server in-process cache. IDistributedCache (Redis) for multi-server. Pattern: check cache first (cache.TryGetValue(key, out var result)), on miss query DB and cache result with TTL, on data change invalidate cache. EF Core Second Level Cache NuGet packages automate this. Cache reference data (countries, categories) aggressively; user-specific data with short TTL or not at all.
Q35:

How do you implement pagination efficiently in EF Core?

Mid

Answer

Use:

Skip((page-1)*pageSize).Take(pageSize)

Combine with filtering and sorting before applying pagination.

Quick Summary: Efficient pagination with EF Core: offset-based: .Skip((page-1)*pageSize).Take(pageSize) - gets slow for large offsets. Keyset pagination: WHERE id > lastSeenId LIMIT pageSize - stays fast regardless of depth. Return totalCount separately (COUNT() query) only when needed (expensive on large tables). Use async: ToListAsync(). Include an index on the sort column for performance.
Q36:

How do you handle migrations in EF Core?

Mid

Answer

Use:

  • Add-Migration
  • Update-Database

Supports schema evolution and controlled deployments.

Quick Summary: EF Core migrations: dotnet ef migrations add MigrationName generates a migration file. dotnet ef database update applies it. Review generated SQL before applying to production. Store migration files in source control. Use CI/CD to apply migrations automatically (Database.MigrateAsync() on startup or migration runner). Never delete applied migration files. Use --idempotent flag for generating idempotent SQL scripts.
Q37:

How do you manage large datasets in EF Core?

Mid

Answer

Best practices:

  • Use projection via Select()
  • Use pagination
  • Avoid ToList() on large tables
  • Optimize queries with indexes
Quick Summary: Large datasets with EF Core: use AsNoTracking() for read-only queries (no change tracking overhead). Stream results with AsAsyncEnumerable() and await foreach instead of loading all into memory. Use pagination (Skip/Take). Use raw SQL (FromSqlRaw) for complex queries that EF Core translates poorly. Enable connection resiliency for transient failures. Split large queries into batches.
Q38:

How do you test Web API with EF Core?

Mid

Answer

Testing strategies:

  • Use InMemory provider for unit tests
  • Use SQLite in-memory for integration tests
  • Mock repositories/DbContext for isolation
Quick Summary: Test Web API with EF Core: use WebApplicationFactory with UseInMemoryDatabase for integration tests (or SQLite for more complete testing). Reset DB state between tests. Unit test: mock IRepository or use an in-memory context. Don't test EF Core queries with just mocks - you need a real DB to verify query translation. TestContainers library spins up a real PostgreSQL/SQL Server container for tests.

Curated Sets for ASP.NET Web API

No curated sets yet. Group questions into collections from the admin panel to feature them here.

Ready to level up? Start Practice