ASP.NET CORE Web API - Final Recap & Mega Quiz
14 topics. 30 questions. This is what takes down Zomato at 8pm on Saturday.
Staff Engineer Rule: If you cannot debug a 500 in prod with just logs + Postman, you are not senior. Memorization = junior. Root cause analysis = principal.
1. Recap Table: 14 API Concepts That Crash Production
| Topic | Key Idea | Production Code | FAANG Trap - Zomato Incident |
|---|---|---|---|
| Minimal API vs Controllers | Controllers = MVC pipeline. Minimal = Endpoint routing only. No filters | app.MapGet("/api/restaurants", () => repo.GetAll()); |
Mixed Minimal + Controllers. Auth filter on controller skipped for Minimal endpoint. GET /api/admin/users exposed. CVE-2024-28001 |
| HTTP Methods | GET=Safe+Idempotent. POST=Not. PUT=Idempotent. PATCH=Partial | [HttpPut("cart/{id}")] vs [HttpPatch] |
Used POST for Zomato cart update. Retry on timeout = duplicate items added. $2M refunds. PUT would be idempotent |
| Model Binding | [FromBody]=JSON. [FromQuery]=URL. [FromRoute]=/path/{id}. Only 1 body | public IActionResult Order([FromBody] OrderDto dto) |
[FromBody] + x-www-form-urlencoded = 415. Zomato mobile app crash. OR bound to Entity = Overposting. Hacker sets Order.Total=0.01 |
| Swagger/OpenAPI | Swashbuckle generates from XML + Attributes. Use AddSwaggerGen | app.UseSwagger(); app.UseSwaggerUI(); |
Swagger enabled in prod. /swagger/v1/swagger.json leaked admin endpoints. Bots enumerated. Mass scraping. 100k req/s DDoS |
| Authentication & JWT | Bearer token. ValidateIssuer, Audience, Lifetime, IssuerSigningKey | builder.Services.AddAuthentication("Bearer").AddJwtBearer() |
ValidateAudience=false. Hacker reused JWT from dev env on prod. Took over Zomato Gold accounts. Token forgery |
| Filters & Middleware | Middleware=app pipeline. Filters=MVC pipeline. Order matters | public class ZomatoLogFilter : IAsyncActionFilter |
Exception middleware after UseRouting. Exception in routing = Yellow screen. Or Sync filter with Task.Wait = Thread pool starvation |
| CORS & Security | Browser preflight OPTIONS. AllowCredentials!= AllowAnyOrigin | policy.WithOrigins("https://zomato.com").AllowCredentials() |
AllowAnyOrigin + AllowCredentials = CORS error. OR wildcard *.zomato.com allowed evil.zomato.com. CSRF token stolen |
| API Versioning | URL /v1/, Header, Query. DefaultVersion ignored without AssumeDefaultVersionWhenUnspecified | services.AddApiVersioning(o => o.ApiVersionReader = new UrlSegmentApiVersionReader()) |
v2 breaking change deployed. Mobile app still calls /v1/. 404s spike. No fallback. 30% users can't order |
| Global Error Handling | UseExceptionHandler first. Or IExceptionFilter. Never leak stack traces | app.UseExceptionHandler("/error"); |
500 returned {stackTrace: "at Zomato.Db..."} leaked table names. SQL injection next. Or no logging = silent failures |
| Async & CancellationToken | async Task, not async void. Pass CancellationToken to EF/HttpClient | public async Task |
User closed Zomato app. Request cancelled but DB query still runs 30s. Snowball = DB connection pool exhausted. 503s |
| Status Codes | 200 OK, 201 Created, 204 NoContent, 400 BadRequest, 401 Unauth, 403 Forbidden, 404, 409 Conflict, 500 | return CreatedAtAction(nameof(Get), new {id}, dto); |
Return 200 for POST = client can't get Location header. Return 500 for validation error = monitoring alerts fire. Pager fatigue |
| Rate Limiting | .NET 7+ RateLimiter. FixedWindow, Sliding, TokenBucket, Concurrency | app.UseRateLimiter(new RateLimiterOptions()) |
No rate limit. Bot spams /api/restaurant/search?q=a. DB CPU 100%. Real users 504 Timeout. Zomato down 20min |
| Content Negotiation | Accept header. Json, XML. Return 406 if not supported | services.AddControllers().AddXmlSerializerFormatters() |
Client sends Accept: application/xml. You return JSON anyway. Client crash. OR 406 with no body = hard to debug |
| ProblemDetails | RFC7807. Standard error format. type, title, status, detail, instance | services.AddProblemDetails(); app.UseExceptionHandler(); |
Custom error {msg: "fail"} = frontend can't parse. ProblemDetails gives traceId for correlation. Saves 2 hours debugging |
2. The 6 Production Killers - Zomato Code Examples
A. Captive Dependency - Restaurant Data Leak
// STARTUP - THE BUG
builder.Services.AddSingleton<IRestaurantCache, RestaurantCache>();
builder.Services.AddScoped<IZomatoDbContext, ZomatoDbContext>();
public class RestaurantCache : IRestaurantCache {
private readonly IZomatoDbContext _db; // Captures first request's DbContext
public RestaurantCache(IZomatoDbContext db) => _db = db;
public async Task<List<Restaurant>> GetForUserAsync(int userId) {
// Request #1: User 123. _db cached.
// Request #2: User 456 calls this. _db still has User 123 data in ChangeTracker
return await _db.Restaurants.Where(r => r.CityId == GetUserCity(userId)).ToListAsync(); // Returns wrong city
}
}
// FIX: Inject IServiceScopeFactory, create scope per method call
B. Overposting - Free Zomato Order
// ENTITY
public class Order { public int Id {get;set;} public decimal Total {get;set;} public bool IsPaid {get;set;} }
// BAD API - Binds to Entity
[HttpPost] public IActionResult Create(Order order) { // Hacker posts {total:0.01, isPaid:true}
_db.Orders.Add(order); _db.SaveChanges(); // BOOM - $0.01 order marked paid
}
// GOOD - Input DTO = Whitelist
public class OrderCreateDto { public List<CartItemDto> Items {get;set;} }
[HttpPost] public IActionResult Create(OrderCreateDto dto) {
var order = new Order {
Total = CalculateTotal(dto.Items), // Server calculates
IsPaid = false // Server controls
};
_db.Orders.Add(order); _db.SaveChanges();
}
C. JWT ValidateAudience=false - Account Takeover
// BAD CONFIG
services.AddAuthentication("Bearer").AddJwtBearer(o => {
o.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuer = true,
ValidateAudience = false, // BUG
IssuerSigningKey = key
};
});
// Attack: Get JWT from dev.zomato.com. Audience=dev. Replay to api.zomato.com.
// Server accepts because audience not checked. Hacker = you.
// FIX: ValidateAudience = true, ValidAudience = "api.zomato.com"
D. CORS AllowAnyOrigin + AllowCredentials - Cookie Theft
// BAD
builder.Services.AddCors(o => o.AddPolicy("AllowAll", p => {
p.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader().AllowCredentials();
}));
// Browser blocks this: Cannot use wildcard origin with credentials.
// But if you use.SetIsOriginAllowed(_ => true), it passes.
// Hacker site evil.com makes fetch('https://api.zomato.com/user', {credentials:'include'})
// Browser sends Zomato cookies to hacker. Session hijack.
// FIX:.WithOrigins("https://www.zomato.com", "https://m.zomato.com").AllowCredentials()
E. Exception Middleware Order - Yellow Screen in Prod
// BAD ORDER
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(e => e.MapControllers());
app.UseExceptionHandler("/error"); // TOO LATE
// Exception thrown in UseRouting or Auth = not caught. Unhandled exception = 500 YSOD
// FIX: app.UseExceptionHandler("/error"); MUST be first after app.UseDeveloperExceptionPage
F. No CancellationToken - DB Meltdown
// BAD
[HttpGet("restaurants")] public async Task<IActionResult> Search([FromQuery] string q) {
return Ok(await _db.Restaurants.Where(r => r.Name.Contains(q)).ToListAsync()); // No CT
}
// User searches, closes app. Request cancelled. But EF query still runs 30s.
// 10k users do this = 10k DB connections stuck. Pool exhausted. All requests 503.
// FIX: ToListAsync(cancellationToken). Pass CancellationToken ct to action.
Mega Quiz ๐ง - 30 Questions. Zomato API Edition.
Next: Interview Questions - If you missed Q20-Q30, FAANG will reject you. These are Stripe/Uber/Zomato actual incidents.
No comments yet. Be the first to share your thoughts!