How to use parameter binding in minimal APIs in ASP.NET Core
How to use parameter binding in minimal APIs in ASP.NET Core
Minimal APIs are a type of API in ASP.NET Core that includes a bare minimum of files, features, and dependencies. Minimal APIs allow you to build fully functional REST endpoints with minimal coding and configuration. One of many new enhancements in ASP.NET Core 7 is its support for parameter binding in minimal APIs.
The goal of this post is to give you a head start on working with parameter binding in minimal APIs. To use the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here.
Create a minimal Web API project in Visual Studio 2022
First off, let’s create an ASP.NET Core project in Visual Studio 2022. Follow these steps:
- Launch the Visual Studio 2022 IDE.
- Click on “Create new project.”
- In the “Create new project” window, select “ASP.NET Core Web API” from the list of templates displayed.
- Click Next.
- In the “Configure your new project” window, specify the name and location for the new project.
- Optionally check the “Place solution and project in the same directory” check box, depending on your preferences.
- Click Next.
- In the “Additional Information” window shown next, uncheck the check box that says “Use controllers…” since we’ll be using minimal APIs in this example. Leave the “Authentication Type” set as “None” (default). Also uncheck the “Configure for HTTPS” and “Enable Open API Support” check boxes. Finally, ensure that the “Enable Docker” check box is unchecked as we won’t be using Docker here.
- Click Create.
We’ll use this ASP.NET Core Web API project to work with parameter binding in the sections below.
What is parameter binding?
Parameter binding involves mapping incoming HTTP request data to action method parameters, allowing developers to process requests and respond in a structured and efficient manner.
Parameter binding simplifies the process of handling HTTP requests and allows developers to focus on building the logic of their API endpoints. Minimal APIs in ASP.NET Core 7 offer several types of parameter binding including FromQuery, FromRoute, FromHeader, and FromBody.
Why use parameter binding?
Here are a few reasons why you should use parameter binding in minimal APIs.
- To simplify code: Using parameter binding, developers can reduce the boilerplate code required to handle incoming HTTP requests. Instead of manually parsing query string parameters, route data, and request bodies, parameter binding allows developers to define action method parameters and have the framework handle the binding process automatically.
- To improve code maintainability: By leveraging parameter binding in minimal APIs, developers can create more maintainable code that is easier to understand and modify over time. The binding process is standardized and predictable, making it easier for developers to reason how data is transmitted between the client and the server.
- To enhance application performance: Parameter binding can also help improve performance by reducing unnecessary data processing in the application. For example, by binding a request body to a specific parameter type, the framework can avoid the overhead of parsing and deserializing the entire request body, instead focusing only on the relevant data needed by the application.
- To handle complex data types: Parameter binding can be used to handle complex data types such as nested objects, arrays, and collections. By leveraging the built-in mechanisms for binding complex data types, developers can create APIs that address a wide range of data formats without having to write additional code.
How does parameter binding work?
Parameter binding in minimal APIs in ASP.NET Core 7 works similarly to that of traditional ASP.NET Core applications. When a client makes an HTTP request to a minimal API, the request data is automatically mapped to action method parameters based on the parameter names and types. By default, the framework uses a convention-based approach to automatically map request data to action method parameters, but developers can also use explicit parameter binding to gain more control over this process.
Parameter binding with query strings
To use parameter binding in minimal APIs in ASP.NET Core 7, developers need to define action methods that accept parameters. For example, the following code snippet defines a minimal API endpoint that accepts a parameter from the query string.
var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); app.MapGet("/hello", ([FromQuery] string name) => { return $"Hello {name}"; }); app.Run();
In this example, the [FromQuery] attribute tells the framework to bind the name parameter to the value of the name query string parameter in the HTTP request.
Parameter binding with dependency injection
With ASP.NET Core 7, you can take advantage of dependency injection to bind parameters in the action methods of your API controllers. If the type is configured as a service, you no longer need to add the [FromServices] attribute to your method parameters. Consider the following code snippet.
[Route("[controller]")] [ApiController] public class MyDemoController : ControllerBase { public ActionResult Get(IDateTime dateTime) => Ok(dateTime.Now); }
If the type is configured as a service, you don’t need to use the [FromServices] attribute to bind parameters. Instead, you can use the following piece of code to bind parameters using dependency injection.
var builder = WebApplication.CreateBuilder(args); builder.Services.AddSingleton<IDateTime, SystemDateTime>(); var app = builder.Build(); app.MapGet("/", (IDateTime dateTime) => dateTime.Now); app.MapGet("/demo", ([FromServices] IDateTime dateTime) => dateTime.Now); app.Run();
Explicit parameter binding in minimal APIs
Explicit parameter binding in minimal APIs in ASP.NET Core 7 is a technique that allows developers to have more control over the binding process by explicitly specifying the source of the data for a given parameter.
This is useful in situations where the binding behavior cannot be inferred from the parameter’s name or type alone. In ASP.NET Core 7 minimal APIs, developers can use the following attributes to explicitly specify the source of data for a parameter:
- [FromQuery] specifies that the parameter value should be derived from the HTTP query string.
- [FromRoute] specifies that the parameter value should be derived from the HTTP request’s route data.
- [FromHeader] specifies that the parameter value should be taken from the HTTP request header.
- [FromBody] specifies that the parameter value should come from the HTTP request body.
For example, consider the following minimal API endpoint that accepts an instance of type Author in the request body.
app.MapPost("/demo", ([FromBody] Author author) => { // Write your code here to process the author object });
In this case, the [FromBody] attribute tells the framework to bind the parameter to the data in the request body. If this attribute is not specified, the framework will try to bind the parameter using other available sources, such as query string or route data, which is likely not what we want in this scenario.
Note that you can also use the [AsParameters] attribute to map query parameters directly to an object without having to use the BindAsync or TryParse methods.
app.MapGet("/display", ([AsParameters] Author author) => { return $"First Name: {author.FirstName}, Last Name: {author.LastName}"; });
Custom model binding in minimal APIs
Custom model binding allows developers to define their own binding logic for complex data types or scenarios that cannot be handled by the default binding mechanisms. Custom binding is particularly useful when working with APIs that require data transformation or normalization before the data can be used by the application.
In ASP.NET Core 7 minimal APIs, custom model binding is achieved by implementing the IModelBinder interface or by using the IModelBinderProvider interface to provide a custom implementation of the IModelBinder interface for a specific data type.
To create a custom model binder, you need to implement the IModelBinder interface and override the BindModelAsync method. This method takes a BindingContext parameter, which contains information about the request and the model being bound.
In the BindModelAsync method, you can perform any necessary data transformation or validation before returning the bound model. Below is an example of a custom model binder that binds an incoming JSON payload to a Customer object.
public class CustomerModelBinder : IModelBinder { public async Task BindModelAsync(ModelBindingContext bindingContext) { var json = await new StreamReader(bindingContext.HttpContext.Request.Body). ReadToEndAsync(); var customer = JsonConvert.DeserializeObject<Customer>(json); bindingContext.Result = ModelBindingResult.Success(customer); } }
In this example, the CustomerModelBinder class implements the IModelBinder interface and provides a custom implementation of the BindModelAsync method. The method reads the JSON payload from the HTTP request body and deserializes it into a Customer object using the Newtonsoft.Json library. The resulting Customer object is then returned as a successful ModelBindingResult.
To use this custom model binder in a minimal API endpoint, you can use the [ModelBinder] attribute on the parameter.
app.MapPost("/demo", ([ModelBinder(typeof(CustomerModelBinder))] Customer customer) => { // Write your code here to process the Customer object });
In the preceeding code example, the [ModelBinder] attribute specifies that the Customer parameter should be bound using the CustomerModelBinder class.
Parameter binding simplifies the writing of code for handling HTTP requests. It allows developers to more easily handle complex data types, while simplifying code and improving code maintainability, while building the logic of their API endpoints. By taking advantage of parameter binding in minimal APIs, developers can create efficient, maintainable, and scalable APIs that meet the needs of their applications and users.