Section 2 of 6

Request/Response Cycle

🎯 What You'll Learn

  • Complete request/response lifecycle in ASP.NET Core
  • How requests flow through the pipeline
  • Routing and endpoint selection
  • Model binding
  • Action execution
  • Response generation
  • Practical InvenTrack examples

The Complete Lifecycle

Request/Response Flow Text
1. HTTP Request arrives
   ↓
2. Middleware Pipeline
   ├─ Exception Handler
   ├─ HTTPS Redirection
   ├─ Static Files (may short-circuit)
   ├─ Routing
   ├─ CORS
   ├─ Authentication
   └─ Authorization
   ↓
3. Endpoint Routing
   ├─ Match URL to endpoint
   └─ Select controller/action
   ↓
4. Model Binding
   ├─ Extract data from request
   ├─ Convert to C# objects
   └─ Validate data
   ↓
5. Action Execution
   ├─ Execute controller method
   ├─ Business logic
   └─ Generate result
   ↓
6. Result Execution
   ├─ Convert result to HTTP response
   ├─ Serialize data (JSON, XML)
   └─ Set status code & headers
   ↓
7. Response flows back through middleware
   ↓
8. HTTP Response sent to client

Step-by-Step Example

Let's trace a request through InvenTrack:

HTTP Request HTTP
GET /api/products/123 HTTP/1.1
Host: inventrackapp.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Step 1: Request Arrives

The HTTP request enters ASP.NET Core's web server (Kestrel).

Step 2: Middleware Pipeline

Program.cs C#
app.UseExceptionHandler("/error");  // 1. Exception handling
app.UseHttpsRedirection();           // 2. Redirect to HTTPS
app.UseStaticFiles();               // 3. Check for static files
app.UseRouting();                   // 4. Match endpoint
app.UseCors();                      // 5. CORS headers
app.UseAuthentication();           // 6. Identify user
app.UseAuthorization();            // 7. Check permissions
app.MapControllers();               // 8. Execute endpoint

Step 3: Routing

ASP.NET Core matches the URL /api/products/123 to a controller action:

Controllers/ProductsController.cs C#
[ApiController]
[Route("api/[controller]")]  // Matches "api/products"
public class ProductsController : ControllerBase
{
    [HttpGet("{id}")]  // Matches "123"
    public async Task<IActionResult> GetProduct(int id)
    {
        // This method will be executed!
    }
}

Step 4: Model Binding

ASP.NET Core extracts 123 from the URL and binds it to the id parameter:

Model Binding C#
// URL: /api/products/123
// Route template: {id}
// Result: id = 123

[HttpGet("{id}")]
public async Task<IActionResult> GetProduct(int id)  // id = 123
{
    // ...
}

Step 5: Action Execution

Execute Business Logic C#
[HttpGet("{id}")]
public async Task<IActionResult> GetProduct(int id)
{
    // 1. Retrieve from database
    var product = await _productService.GetByIdAsync(id);
    
    // 2. Check if found
    if (product == null)
        return NotFound();
    
    // 3. Return result
    return Ok(product);
}

Step 6: Result Execution

ASP.NET Core converts the result to an HTTP response:

Result → HTTP Response C#
// Ok(product) becomes:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 145

{
  "id": 123,
  "name": "Laptop",
  "price": 999.99,
  "quantityInStock": 50
}

Step 7: Response Through Middleware

The response flows back through the middleware pipeline in reverse order.

Step 8: Response Sent

The HTTP response is sent back to the client.

Model Binding in Detail

Model binding extracts data from various sources:

1. Route Parameters

From Route C#
// URL: /api/products/123
[HttpGet("{id}")]
public IActionResult GetProduct(int id)  // id = 123
{
    return Ok();
}

2. Query String

From Query C#
// URL: /api/products?page=2&pageSize=10
[HttpGet]
public IActionResult GetProducts(int page, int pageSize)
{
    // page = 2, pageSize = 10
    return Ok();
}

3. Request Body

From Body C#
// POST /api/products
// Body: { "name": "Laptop", "price": 999.99 }

[HttpPost]
public IActionResult CreateProduct([FromBody] ProductDto product)
{
    // product.Name = "Laptop"
    // product.Price = 999.99
    return Created();
}

4. Headers

From Header C#
// Header: X-API-Key: secret123

[HttpGet]
public IActionResult GetProducts([FromHeader(Name = "X-API-Key")] string apiKey)
{
    // apiKey = "secret123"
    return Ok();
}

5. Complex Objects

Multiple Sources C#
// URL: /api/products/123?includeDetails=true
// Body: { "quantity": 10 }

[HttpPut("{id}")]
public IActionResult UpdateStock(
    int id,                                    // From route
    bool includeDetails,                       // From query
    [FromBody] StockUpdateDto update)    // From body
{
    return Ok();
}

Response Generation

ASP.NET Core provides helper methods to generate responses:

Method Status Code Use Case
Ok(data) 200 Successful GET request
Created(uri, data) 201 Resource created (POST)
NoContent() 204 Successful DELETE/PUT
BadRequest() 400 Invalid request data
Unauthorized() 401 Not authenticated
Forbid() 403 Not authorized
NotFound() 404 Resource not found
Conflict() 409 Resource conflict

Complete InvenTrack Example

Controllers/ProductsController.cs C#
[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly IProductService _productService;
    private readonly ILogger<ProductsController> _logger;

    public ProductsController(
        IProductService productService,
        ILogger<ProductsController> logger)
    {
        _productService = productService;
        _logger = logger;
    }

    // GET /api/products
    [HttpGet]
    public async Task<IActionResult> GetProducts(
        [FromQuery] int page = 1,
        [FromQuery] int pageSize = 10)
    {
        _logger.LogInformation("Getting products - Page: {Page}, PageSize: {PageSize}", page, pageSize);
        
        var products = await _productService.GetPagedAsync(page, pageSize);
        
        return Ok(products);
    }

    // GET /api/products/123
    [HttpGet("{id}")]
    public async Task<IActionResult> GetProduct(int id)
    {
        _logger.LogInformation("Getting product with ID: {Id}", id);
        
        var product = await _productService.GetByIdAsync(id);
        
        if (product == null)
        {
            _logger.LogWarning("Product not found: {Id}", id);
            return NotFound(new { message = $"Product with ID {id} not found" });
        }
        
        return Ok(product);
    }

    // POST /api/products
    [HttpPost]
    public async Task<IActionResult> CreateProduct([FromBody] CreateProductDto dto)
    {
        _logger.LogInformation("Creating product: {Name}", dto.Name);
        
        var product = await _productService.CreateAsync(dto);
        
        return CreatedAtAction(
            nameof(GetProduct),
            new { id = product.Id },
            product);
    }

    // PUT /api/products/123
    [HttpPut("{id}")]
    public async Task<IActionResult> UpdateProduct(
        int id,
        [FromBody] UpdateProductDto dto)
    {
        _logger.LogInformation("Updating product: {Id}", id);
        
        var success = await _productService.UpdateAsync(id, dto);
        
        if (!success)
            return NotFound();
        
        return NoContent();
    }

    // DELETE /api/products/123
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteProduct(int id)
    {
        _logger.LogInformation("Deleting product: {Id}", id);
        
        var success = await _productService.DeleteAsync(id);
        
        if (!success)
            return NotFound();
        
        return NoContent();
    }
}

Key Takeaways

  • 8-step lifecycle: Request → Middleware → Routing → Binding → Execution → Result → Middleware → Response
  • Middleware pipeline: Processes request before reaching controller
  • Routing: Matches URL to controller/action
  • Model binding: Extracts data from route, query, body, headers
  • Action execution: Business logic runs
  • Result execution: Converts result to HTTP response
  • Use Ok(), Created(), NotFound(), etc. for responses
  • Model binding sources: [FromRoute], [FromQuery], [FromBody], [FromHeader]
  • Response flows back through middleware in reverse
  • Logging helps trace request flow
🎯 Next Steps

You now understand the complete request/response cycle! In the next section, we'll explore HTTP Methods—GET, POST, PUT, DELETE, PATCH, and when to use each one.