Introduction
In Day 1, we learned what .NET Core Web API is, why it is used, and how it acts as a bridge between a client application and a backend system.
Now let's understand something more important:
What actually happens when a client sends a request to a .NET Core Web API?
Suppose you are building an Employee Management application. An Angular application displays employee information, and the backend is developed using .NET Core Web API.
When a user opens the employee list, Angular sends an HTTP request to the API. The API receives that request, processes it, communicates with the application logic or database when required, and sends a response.
But several things happen between receiving the request and returning the response.
In this article, we'll understand that complete process step by step.
1. Understand the client-server communication
Before exploring the internal API pipeline, let's understand the two sides involved.
Client application
The client is the application that makes a request to the API. It could be:
Angular application
React application
Mobile application
Desktop application
Another backend service
Postman or another API testing tool
Backend API
The .NET Core Web API receives the request, executes the required logic, and returns a response.
For example, when a user opens the employee list, the Angular application sends:
GET https://localhost:7001/api/employeesThe API processes this request and might return:
[
{
"id": 101,
"name": "Rahul",
"department": "IT"
},
{
"id": 102,
"name": "Priya",
"department": "HR"
}
]The Angular application reads this JSON response and displays the employees on the screen.
The important point is that the client and API communicate through HTTP, using a request-response model.
2. Understand the complete request-response lifecycle
Let's follow one practical example.
Imagine a user wants to view the details of employee ID 101.
The Angular application sends:
GET /api/employees/101The request travels through several stages inside the .NET application.
Request processing flow:
Client sends an HTTP request.
Kestrel receives the incoming request.
Middleware components process the request.
Routing identifies the matching endpoint.
The framework prepares the controller action and its parameters.
The controller executes the required application logic.
The application retrieves the employee data.
The result is converted into an HTTP response.
The response travels back to the client.
Let's understand each stage.
3. Step 1 – Client sends an HTTP request
The process starts when the client application makes an API call.
For example, an Angular application might use:
this.http.get<Employee>(
'https://localhost:7001/api/employees/101'
).subscribe({
next: (employee) => {
console.log(employee);
},
error: (error) => {
console.error(error);
}
});Here:
http.get()sends an HTTP GET request.The URL identifies the API endpoint.
The API receives the request.
The response is handled inside the subscription.
The client does not directly access the backend database. It communicates with the API, which controls how data is accessed.
4. Step 2 – Kestrel receives the request
When a request reaches a .NET application, it needs a web server to receive and handle the incoming HTTP connection.
In modern ASP.NET Core applications, the default cross-platform web server is Kestrel.
Kestrel is responsible for accepting network connections and receiving HTTP requests. It then passes the request into the ASP.NET Core request-processing pipeline.
For example, when your application runs locally:
https://localhost:7001Kestrel listens on the configured endpoint and handles incoming requests.
You can see the application startup configuration in Program.cs.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();This is a simplified controller-based application setup.
When the application starts, it creates and configures the web host, which runs the application and its request-processing pipeline.
Important: Kestrel is the web server. It does not contain your employee business logic. Its job is to receive and handle HTTP traffic.
5. Step 3 – Middleware processes the request
This is one of the most important concepts in .NET Core Web API.
Middleware components are software components arranged in a pipeline. Each component can inspect an HTTP request, perform an operation, pass control to the next component, and potentially process the response while it travels back.
Common middleware responsibilities include:
Exception handling
HTTPS redirection
Static file handling
Authentication
Authorization
CORS
Request logging
Consider this example:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.MapControllers();
app.Run();Here, middleware and endpoint configuration determine how incoming requests are handled.
For example, HTTPS redirection can redirect an HTTP request to HTTPS before it reaches the controller.
How does middleware work?
Imagine three middleware components:
Incoming Request
|
v
Middleware A
|
v
Middleware B
|
v
Middleware C
|
v
Controller / Endpoint
|
v
Outgoing ResponseThe request moves forward through the pipeline.
After the endpoint finishes processing, the response travels back through the middleware components in reverse order.
This is why middleware can perform work both before and after the next component executes.
Practical middleware example
Let's create a simple request-logging middleware.
Add the following code before app.Run() in Program.cs:
app.Use(async (context, next) =>
{
Console.WriteLine(
$"Incoming Request: {context.Request.Method} " +
$"{context.Request.Path}");
await next();
Console.WriteLine(
$"Outgoing Response: {context.Response.StatusCode}");
});Now when a client calls:
GET /api/employees/101You may see:
Incoming Request: GET /api/employees/101
Outgoing Response: 200This is a simple example of how middleware can help monitor API requests and responses.
Notice the await next() statement. It allows the request to continue to the next middleware component. Once downstream processing completes, execution resumes after that statement.
6. Step 4 – Routing identifies the endpoint
After the request passes through the relevant middleware, routing determines which endpoint should handle it.
Consider this controller:
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("api/[controller]")]
public class EmployeesController : ControllerBase
{
[HttpGet("{id:int}")]
public IActionResult GetEmployee(int id)
{
return Ok(new
{
Id = id,
Name = "Rahul",
Department = "IT"
});
}
}Let's understand the routing attributes.
[Route("api/[controller]")]
The [controller] token is replaced with the controller name without the Controller suffix.
For EmployeesController, the route becomes:
api/employees[HttpGet("{id:int}")]
This identifies an HTTP GET endpoint with an integer route parameter.
The complete URL becomes:
GET /api/employees/101When this request reaches the application, routing matches it with the GetEmployee action.
If the URL does not match an available endpoint, the application can return a 404 Not Found response.
7. Step 5 – Model binding prepares the action parameters
Once an endpoint has been selected, ASP.NET Core prepares the parameters required by the action method.
This process is called Model Binding.
For example:
[HttpGet("{id:int}")]
public IActionResult GetEmployee(int id)
{
return Ok(new
{
Id = id,
Name = "Rahul"
});
}The client sends:
GET /api/employees/101The framework extracts 101 from the route and binds it to the id parameter.
The action receives:
id = 101Model binding can obtain data from different request sources, including:
Route parameters
Query strings
Request bodies
Form fields
Request headers
For example, consider a query-string parameter:
[HttpGet]
public IActionResult GetEmployees([FromQuery] string department)
{
return Ok(new
{
Department = department
});
}Request:
GET /api/employees?department=ITThe value IT is bound to the department parameter.
You can explicitly specify binding sources using attributes such as [FromRoute], [FromQuery], [FromBody], and [FromHeader].
8. Step 6 – Controller executes the application logic
After routing and parameter preparation, the selected controller action executes.
In a real application, a controller should not contain all the business logic or database operations.
A better approach is to keep responsibilities separate.
For example:
EmployeesController
|
v
EmployeeService
|
v
EmployeeRepository
|
v
SQL ServerEach component has a separate responsibility.
Controller: Handles the incoming HTTP request and produces the HTTP response.
Service: Contains application or business logic.
Repository: Handles data access when a repository pattern is used.
Database: Stores the employee records.
This separation makes the application easier to maintain, test, and extend.
We'll explore Dependency Injection in Day 8 of this series.
For now, understand that the controller is the entry point for the application endpoint, not necessarily the place where all business operations should be implemented.
9. Step 7 – API creates and returns the response
After processing the request, the controller returns a result.
For example:
[HttpGet("{id:int}")]
public IActionResult GetEmployee(int id)
{
var employee = new
{
Id = id,
Name = "Rahul",
Department = "IT"
};
return Ok(employee);
}The Ok() method creates an HTTP 200 OK response.
ASP.NET Core's result execution and output formatting mechanisms serialize the returned object into JSON by default for a typical JSON API response.
The client receives:
HTTP/1.1 200 OK
Content-Type: application/jsonResponse body:
{
"id": 101,
"name": "Rahul",
"department": "IT"
}The client can now use this data to display employee details.
Common HTTP response status codes
An API response contains a status code that indicates the outcome of the request.
| Status code | Meaning | Example |
|---|---|---|
| 200 | OK | Employee retrieved successfully |
| 201 | Created | New employee created |
| 204 | No Content | Operation completed without a response body |
| 400 | Bad Request | Invalid request data |
| 401 | Unauthorized | Authentication is required or invalid |
| 403 | Forbidden | User is not allowed to access the resource |
| 404 | Not Found | Employee does not exist |
| 500 | Internal Server Error | Unexpected server-side failure |
We'll explore HTTP methods in detail in Day 3 and authentication and authorization later in Week 3.
10. Complete practical example
Let's connect the concepts with a small controller-based example.
Create an EmployeesController.cs file:
using Microsoft.AspNetCore.Mvc;
namespace EmployeeManagement.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class EmployeesController : ControllerBase
{
private static readonly List<Employee> Employees =
[
new Employee
{
Id = 101,
Name = "Rahul",
Department = "IT"
},
new Employee
{
Id = 102,
Name = "Priya",
Department = "HR"
}
];
[HttpGet]
public IActionResult GetEmployees()
{
return Ok(Employees);
}
[HttpGet("{id:int}")]
public IActionResult GetEmployeeById(int id)
{
var employee = Employees.FirstOrDefault(x => x.Id == id);
if (employee is null)
{
return NotFound(new
{
Message = $"Employee with ID {id} was not found."
});
}
return Ok(employee);
}
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Department { get; set; } = string.Empty;
}Make sure your Program.cs registers controllers and maps their routes:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.MapControllers();
app.Run();This example uses an in-memory list to keep the focus on request processing. It does not persist data in SQL Server, and the list resets when the application restarts.
Test the API
Run the application and send these requests using Postman or another API client.
Request 1: Get all employees
GET https://localhost:7001/api/employeesExpected response: 200 OK
[
{
"id": 101,
"name": "Rahul",
"department": "IT"
},
{
"id": 102,
"name": "Priya",
"department": "HR"
}
]Request 2: Get employee by ID
GET https://localhost:7001/api/employees/101Expected response: 200 OK
{
"id": 101,
"name": "Rahul",
"department": "IT"
}Request 3: Get an employee that does not exist
GET https://localhost:7001/api/employees/999Expected response: 404 Not Found
{
"message": "Employee with ID 999 was not found."
}The port number 7001 is only an example. Use the HTTPS URL and port displayed when your own application starts.
11. Important concepts to remember
Before moving forward, understand these concepts:
HTTP: Communication protocol used between the client and server.
Kestrel: The web server that receives HTTP requests.
Middleware: Components that process requests and responses in a pipeline.
Routing: Matches an incoming request to an endpoint.
Model Binding: Maps request data to action parameters and models.
Controller: Handles requests through its action methods.
Service: Holds application or business logic.
Serialization: Converts objects into a response format such as JSON.
One more important concept is that HTTP APIs are generally designed to be stateless. The server should not rely on remembering the previous request to understand the next one. Each request should carry the information needed to process it, with authentication and other context handled through appropriate mechanisms.
12. Common mistakes beginners should avoid
1. Putting all business logic inside controllers
This makes controllers difficult to maintain as the application grows. Separate business logic into appropriate services.
2. Confusing middleware with controllers
Middleware operates within the request pipeline. Controllers handle requests that match controller endpoints.
3. Thinking routing executes database queries
Routing only identifies the endpoint. Database operations happen in the application logic or data-access layer.
4. Ignoring HTTP status codes
Always return meaningful status codes so that clients can understand whether an operation succeeded or failed.
5. Assuming an API automatically stores data
Returning an object from an API does not save it permanently. Persistence requires an appropriate data-storage mechanism, such as SQL Server.
13. Interview questions
Q1. How does a .NET Core Web API process an incoming request?
An incoming request is received by Kestrel and passed through the ASP.NET Core middleware pipeline. Routing identifies the endpoint, the framework prepares the action parameters, and the controller executes the required logic. The result is then converted into an HTTP response and sent back to the client.
Q2. What is Kestrel?
Kestrel is the cross-platform web server used by ASP.NET Core applications. It receives and handles incoming HTTP connections and passes requests into the application pipeline.
Q3. What is middleware in .NET Core?
Middleware is a component that participates in the HTTP request-response pipeline. It can inspect or modify requests and responses, perform operations, and pass control to the next component.
Q4. What is routing?
Routing matches an incoming HTTP request to an available endpoint based on information such as its URL and HTTP method.
Q5. What is model binding?
Model binding maps data from an HTTP request, such as route values, query strings, and request bodies, to action parameters or models.
Q6. What is the difference between middleware and a controller?
Middleware is used for cross-cutting request-processing operations, such as logging and exception handling. A controller handles requests mapped to its actions and returns application-specific HTTP responses.
Q7. Why do we use JSON in Web APIs?
JSON is a lightweight, widely supported data-interchange format that allows different applications and technologies to exchange structured data.
Conclusion
Now you understand how a .NET Core Web API works internally, from the moment a client sends an HTTP request until the server returns a response.
The complete process can be summarized as:
Client → Kestrel → Middleware → Routing → Model Binding → Controller → Application Logic → HTTP Response → Client
Understanding this lifecycle is important because almost every feature you build in a Web API depends on these concepts.
In the next article, we'll explore one of the most fundamental parts of API development: HTTP Methods – GET, POST, PUT, PATCH, and DELETE. We'll understand when to use each method and how they work through practical C# examples.
Skills Tutorials – Learn • Build • Grow Together
Continue learning with our 30-day .NET Core Web API series.