Welcome to this course on Model Binding in Minimal APIs. Previously, we covered how to build Minimal APIs, define complex routes with default/optional parameters, respond with different status codes and error details, and more. In this course, we'll focus on model binding—the process of mapping an HTTP request into POCOs (Plain Old CLR Objects), providing input to the endpoint handlers. By the end of this lesson, you'll understand how to dynamically receive data through various sources such as route parameters, query strings, and headers.
Model binding extracts values from an HTTP request and maps them to .NET objects. These objects are then passed as method parameters to the endpoint handler. Here’s an example illustrating model binding:
C#1app.MapGet("/todo/{id}", (string id) => 2{ 3 return $"Retrieving TODO with id {id}"; 4});
In this case, the id
parameter is automatically bound from the route parameter of the HTTP request. If you navigate to /todo/123
, the API will respond with "Retrieving TODO with id 123".
To effectively utilize model binding, it's crucial to understand what an HTTP request consists of. An HTTP request typically includes:
- URL Parameters: Part of the URL path, often used to identify resources. For example, in
/todo/123
,123
is a URL parameter. - Query Parameters: Key-value pairs appended to the URL. For example, in
/todos?status=Completed
,status=Completed
is a query parameter. - Headers: Metadata about the request, such as authentication tokens, content type, etc. For example,
Authorization: Bearer token123
. - Body: The main content of the request, often used for POST or PUT methods. For example, a JSON body:
{ "name": "New Todo", "status": "Pending" }
. - Cookies: Small pieces of data stored on the client side and sent with requests. For example,
sessionId=abc123
. - Method: HTTP method like GET, POST, PUT, DELETE, which signifies the operation to be performed. For example,
GET /todos
.
By understanding these components, you'll be able to extract the necessary data to create effective and dynamic API endpoints.
In Minimal APIs, you can bind data from various sources within an HTTP request:
- Route Values: Obtained from URL segments or through default values after matching a route.
- Query String Values: Passed at the end of the URL, not used during routing.
- Header Values: Provided in the HTTP request headers.
- Body JSON: A single parameter may be bound to the JSON body of a request.
- Globally Available Services: Certain services are available globally and can be bound directly. This is related to dependency injection, which is out of scope for this course.
- Custom Binding: ASP.NET Core allows for custom binding methods, providing access to standard objects, such as
HttpRequest
andHttpContext
, associated with every request. We will cover this in more detail later in this course.
Understanding these binding sources will help you create more versatile and responsive APIs.
Let's start with a simple example of extracting a query parameter. Consider the following code snippet:
C#1app.MapGet("/todos", (string status) => 2{ 3 var todos = new List<TodoItem> 4 { 5 new TodoItem { Id = 1, Status = "Pending", Description = "Buy groceries" }, 6 new TodoItem { Id = 2, Status = "Completed", Description = "Clean the house" }, 7 new TodoItem { Id = 3, Status = "In Progress", Description = "Write a blog post" } 8 }; 9 10 return todos.Where(todo => todo.Status.Equals(status, StringComparison.OrdinalIgnoreCase)); 11});
In this example, the status
query parameter is extracted and used to filter a list of TodoItem
objects. When a GET request is made to /todos?status=Completed
, the endpoint returns to-do items with the "Completed" status.
Now let's look at a more complicated example that extracts header values, query parameters, and URL parameters using attributes:
C#1app.MapGet("/todos/{status}", 2 ([FromRoute] string status, 3 [FromQuery] int page, 4 [FromHeader(Name = "PageSize")] int pageSize) => 5 { 6 var todos = new List<TodoItem> 7 { 8 new TodoItem { Id = 1, Status = "Pending", Description = "Buy groceries" }, 9 new TodoItem { Id = 2, Status = "Completed", Description = "Clean the house" }, 10 new TodoItem { Id = 3, Status = "In Progress", Description = "Write a blog post" } 11 }; 12 13 var filteredTodos = todos.Where(todo => todo.Status.Equals(status, StringComparison.OrdinalIgnoreCase)); 14 var paginatedTodos = filteredTodos.Skip((page - 1) * pageSize).Take(pageSize); 15 return paginatedTodos; 16 } 17);
In this example:
- The
status
is derived from the route using[FromRoute]
. - The
page
number is derived from the query string using[FromQuery]
. - The
pageSize
is derived from the HTTP headers using[FromHeader(Name = "PageSize")]
.
This allows for more complex logic and flexibility when handling incoming requests.
In this lesson, we explored the basics of model binding in Minimal APIs, covering the definition and purpose of model binding, the components of an HTTP request that can serve as sources for model binding, various binding sources in Minimal APIs, and provided simple and complex examples demonstrating how to bind query, route, and header parameters. You will now engage in practice exercises to apply these concepts hands-on, helping to solidify your understanding of model binding in Minimal APIs.