diff --git a/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/CustomBindingExample.csproj b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/CustomBindingExample.csproj new file mode 100644 index 000000000000..a3a34b647cd2 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/CustomBindingExample.csproj @@ -0,0 +1,9 @@ + + + + net10.0 + enable + enable + + + diff --git a/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/CustomBoundParameters.cs b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/CustomBoundParameters.cs new file mode 100644 index 000000000000..dd7196638ad2 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/CustomBoundParameters.cs @@ -0,0 +1,26 @@ +using System.Reflection; + +namespace CustomBindingExample; + +public class CustomBoundParameter : IBindableFromHttpContext +{ + public string Value { get; init; } = default!; + + public static ValueTask BindAsync(HttpContext context, ParameterInfo parameter) + { + // Custom binding logic here + // This example reads from a custom header + var value = context.Request.Headers["X-Custom-Header"].ToString(); + + // If no header was provided, you could fall back to a query parameter + if (string.IsNullOrEmpty(value)) + { + value = context.Request.Query["customValue"].ToString(); + } + + return ValueTask.FromResult(new CustomBoundParameter + { + Value = value + }); + } +} \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/Program.cs b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/Program.cs new file mode 100644 index 000000000000..e77a28063f0c --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/Program.cs @@ -0,0 +1,34 @@ + +// +using CustomBindingExample; + +var builder = WebApplication.CreateBuilder(args); +var app = builder.Build(); + +app.UseHttpsRedirection(); + +app.MapGet("/", () => "Hello, IBindableFromHttpContext example!"); + +app.MapGet("/custom-binding", (CustomBoundParameter param) => +{ + return $"Value from custom binding: {param.Value}"; +}); + +app.MapGet("/combined/{id}", (int id, CustomBoundParameter param) => +{ + return $"ID: {id}, Custom Value: {param.Value}"; +}); +// +// +app.MapGet("/validated", (ValidatedParameter param) => +{ + if (string.IsNullOrEmpty(param.Value)) + { + return Results.BadRequest("Value cannot be empty"); + } + + return Results.Ok($"Validated value: {param.Value}"); +}); +// + +app.Run(); \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/TestCustomBindingExample.http b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/TestCustomBindingExample.http new file mode 100644 index 000000000000..ee106316a2a2 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/TestCustomBindingExample.http @@ -0,0 +1,24 @@ +@host = https://localhost:7176 + +### Test the root endpoint +GET {{host}}/ +Accept: text/plain + +### Test the custom binding endpoint +GET {{host}}/custom-binding +X-Custom-Header: TestHeaderValue +Accept: text/plain + +### Test the combined parameter endpoint +GET {{host}}/combined/42 +X-Custom-Header: TestHeaderValue +Accept: text/plain + +### Test the validated parameter endpoint - valid case +GET {{host}}/validated +X-Custom-Header: ValidValue +Accept: text/plain + +### Test the validated parameter endpoint - invalid case +GET {{host}}/validated +Accept: text/plain \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/ValidatedParameters.cs b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/ValidatedParameters.cs new file mode 100644 index 000000000000..4a048b9c0a46 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/ValidatedParameters.cs @@ -0,0 +1,29 @@ +using System.Reflection; + +namespace CustomBindingExample; + +public class ValidatedParameter : IBindableFromHttpContext +{ + public string Value { get; init; } = default!; + + public static ValueTask BindAsync(HttpContext context, ParameterInfo parameter) + { + // Get the value from a query parameter + var value = context.Request.Query["value"].ToString(); + + // Perform some basic validation here + if (string.IsNullOrEmpty(value)) + { + // Return an empty instance - the controller action will handle the validation failure + return ValueTask.FromResult(new ValidatedParameter + { + Value = string.Empty + }); + } + + return ValueTask.FromResult(new ValidatedParameter + { + Value = value + }); + } +} \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/appsettings.Development.json b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/appsettings.Development.json new file mode 100644 index 000000000000..0c208ae9181e --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/appsettings.json b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/appsettings.json new file mode 100644 index 000000000000..ec04bc120fb7 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/CustomBindingExample.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/CustomBindingExample.cs new file mode 100644 index 000000000000..e580f24c65d6 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/CustomBindingExample.cs @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/CustomBoundParameters.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/CustomBoundParameters.cs new file mode 100644 index 000000000000..dd7196638ad2 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/CustomBoundParameters.cs @@ -0,0 +1,26 @@ +using System.Reflection; + +namespace CustomBindingExample; + +public class CustomBoundParameter : IBindableFromHttpContext +{ + public string Value { get; init; } = default!; + + public static ValueTask BindAsync(HttpContext context, ParameterInfo parameter) + { + // Custom binding logic here + // This example reads from a custom header + var value = context.Request.Headers["X-Custom-Header"].ToString(); + + // If no header was provided, you could fall back to a query parameter + if (string.IsNullOrEmpty(value)) + { + value = context.Request.Query["customValue"].ToString(); + } + + return ValueTask.FromResult(new CustomBoundParameter + { + Value = value + }); + } +} \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/Program.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/Program.cs new file mode 100644 index 000000000000..e77a28063f0c --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/Program.cs @@ -0,0 +1,34 @@ + +// +using CustomBindingExample; + +var builder = WebApplication.CreateBuilder(args); +var app = builder.Build(); + +app.UseHttpsRedirection(); + +app.MapGet("/", () => "Hello, IBindableFromHttpContext example!"); + +app.MapGet("/custom-binding", (CustomBoundParameter param) => +{ + return $"Value from custom binding: {param.Value}"; +}); + +app.MapGet("/combined/{id}", (int id, CustomBoundParameter param) => +{ + return $"ID: {id}, Custom Value: {param.Value}"; +}); +// +// +app.MapGet("/validated", (ValidatedParameter param) => +{ + if (string.IsNullOrEmpty(param.Value)) + { + return Results.BadRequest("Value cannot be empty"); + } + + return Results.Ok($"Validated value: {param.Value}"); +}); +// + +app.Run(); \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/TestCustomBindingExample.http b/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/TestCustomBindingExample.http new file mode 100644 index 000000000000..254405aed7da --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/TestCustomBindingExample.http @@ -0,0 +1,18 @@ +@HostAddress = https://localhost:7042 + +### Basic endpoint with IBindableFromHttpContext +GET {{HostAddress}}/bindable +X-Custom-Header: TestHeaderValue +Accept: text/plain + +### IBindableFromHttpContext with query parameter +GET {{HostAddress}}/bindable?customValue=TestQueryValue +Accept: text/plain + +### IBindableFromHttpContext with validation - valid +GET {{HostAddress}}/validated?value=ValidValue +Accept: application/json + +### IBindableFromHttpContext with validation - invalid +GET {{HostAddress}}/validated +Accept: application/json \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/ValidatedParameters.cs b/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/ValidatedParameters.cs new file mode 100644 index 000000000000..4a048b9c0a46 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/ValidatedParameters.cs @@ -0,0 +1,29 @@ +using System.Reflection; + +namespace CustomBindingExample; + +public class ValidatedParameter : IBindableFromHttpContext +{ + public string Value { get; init; } = default!; + + public static ValueTask BindAsync(HttpContext context, ParameterInfo parameter) + { + // Get the value from a query parameter + var value = context.Request.Query["value"].ToString(); + + // Perform some basic validation here + if (string.IsNullOrEmpty(value)) + { + // Return an empty instance - the controller action will handle the validation failure + return ValueTask.FromResult(new ValidatedParameter + { + Value = string.Empty + }); + } + + return ValueTask.FromResult(new ValidatedParameter + { + Value = value + }); + } +} \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/appsettings.json b/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/appsettings.json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/CustomBindingExample.csproj b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/CustomBindingExample.csproj new file mode 100644 index 000000000000..1b28a01c81c7 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/CustomBindingExample.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/CustomBoundParameters.cs b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/CustomBoundParameters.cs new file mode 100644 index 000000000000..dd7196638ad2 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/CustomBoundParameters.cs @@ -0,0 +1,26 @@ +using System.Reflection; + +namespace CustomBindingExample; + +public class CustomBoundParameter : IBindableFromHttpContext +{ + public string Value { get; init; } = default!; + + public static ValueTask BindAsync(HttpContext context, ParameterInfo parameter) + { + // Custom binding logic here + // This example reads from a custom header + var value = context.Request.Headers["X-Custom-Header"].ToString(); + + // If no header was provided, you could fall back to a query parameter + if (string.IsNullOrEmpty(value)) + { + value = context.Request.Query["customValue"].ToString(); + } + + return ValueTask.FromResult(new CustomBoundParameter + { + Value = value + }); + } +} \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/Program.cs b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/Program.cs new file mode 100644 index 000000000000..e77a28063f0c --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/Program.cs @@ -0,0 +1,34 @@ + +// +using CustomBindingExample; + +var builder = WebApplication.CreateBuilder(args); +var app = builder.Build(); + +app.UseHttpsRedirection(); + +app.MapGet("/", () => "Hello, IBindableFromHttpContext example!"); + +app.MapGet("/custom-binding", (CustomBoundParameter param) => +{ + return $"Value from custom binding: {param.Value}"; +}); + +app.MapGet("/combined/{id}", (int id, CustomBoundParameter param) => +{ + return $"ID: {id}, Custom Value: {param.Value}"; +}); +// +// +app.MapGet("/validated", (ValidatedParameter param) => +{ + if (string.IsNullOrEmpty(param.Value)) + { + return Results.BadRequest("Value cannot be empty"); + } + + return Results.Ok($"Validated value: {param.Value}"); +}); +// + +app.Run(); \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/TestCustomBindingExample.http b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/TestCustomBindingExample.http new file mode 100644 index 000000000000..ee106316a2a2 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/TestCustomBindingExample.http @@ -0,0 +1,24 @@ +@host = https://localhost:7176 + +### Test the root endpoint +GET {{host}}/ +Accept: text/plain + +### Test the custom binding endpoint +GET {{host}}/custom-binding +X-Custom-Header: TestHeaderValue +Accept: text/plain + +### Test the combined parameter endpoint +GET {{host}}/combined/42 +X-Custom-Header: TestHeaderValue +Accept: text/plain + +### Test the validated parameter endpoint - valid case +GET {{host}}/validated +X-Custom-Header: ValidValue +Accept: text/plain + +### Test the validated parameter endpoint - invalid case +GET {{host}}/validated +Accept: text/plain \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/ValidatedParameters.cs b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/ValidatedParameters.cs new file mode 100644 index 000000000000..4a048b9c0a46 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/ValidatedParameters.cs @@ -0,0 +1,29 @@ +using System.Reflection; + +namespace CustomBindingExample; + +public class ValidatedParameter : IBindableFromHttpContext +{ + public string Value { get; init; } = default!; + + public static ValueTask BindAsync(HttpContext context, ParameterInfo parameter) + { + // Get the value from a query parameter + var value = context.Request.Query["value"].ToString(); + + // Perform some basic validation here + if (string.IsNullOrEmpty(value)) + { + // Return an empty instance - the controller action will handle the validation failure + return ValueTask.FromResult(new ValidatedParameter + { + Value = string.Empty + }); + } + + return ValueTask.FromResult(new ValidatedParameter + { + Value = value + }); + } +} \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/appsettings.Development.json b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/appsettings.Development.json new file mode 100644 index 000000000000..0c208ae9181e --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/appsettings.json b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/appsettings.json new file mode 100644 index 000000000000..ec04bc120fb7 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/CustomBindingExample.csproj b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/CustomBindingExample.csproj new file mode 100644 index 000000000000..6568b3dcfb4b --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/CustomBindingExample.csproj @@ -0,0 +1,9 @@ + + + + net9.0 + enable + enable + + + diff --git a/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/CustomBoundParameters.cs b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/CustomBoundParameters.cs new file mode 100644 index 000000000000..dd7196638ad2 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/CustomBoundParameters.cs @@ -0,0 +1,26 @@ +using System.Reflection; + +namespace CustomBindingExample; + +public class CustomBoundParameter : IBindableFromHttpContext +{ + public string Value { get; init; } = default!; + + public static ValueTask BindAsync(HttpContext context, ParameterInfo parameter) + { + // Custom binding logic here + // This example reads from a custom header + var value = context.Request.Headers["X-Custom-Header"].ToString(); + + // If no header was provided, you could fall back to a query parameter + if (string.IsNullOrEmpty(value)) + { + value = context.Request.Query["customValue"].ToString(); + } + + return ValueTask.FromResult(new CustomBoundParameter + { + Value = value + }); + } +} \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/Program.cs b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/Program.cs new file mode 100644 index 000000000000..e77a28063f0c --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/Program.cs @@ -0,0 +1,34 @@ + +// +using CustomBindingExample; + +var builder = WebApplication.CreateBuilder(args); +var app = builder.Build(); + +app.UseHttpsRedirection(); + +app.MapGet("/", () => "Hello, IBindableFromHttpContext example!"); + +app.MapGet("/custom-binding", (CustomBoundParameter param) => +{ + return $"Value from custom binding: {param.Value}"; +}); + +app.MapGet("/combined/{id}", (int id, CustomBoundParameter param) => +{ + return $"ID: {id}, Custom Value: {param.Value}"; +}); +// +// +app.MapGet("/validated", (ValidatedParameter param) => +{ + if (string.IsNullOrEmpty(param.Value)) + { + return Results.BadRequest("Value cannot be empty"); + } + + return Results.Ok($"Validated value: {param.Value}"); +}); +// + +app.Run(); \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/TestCustomBindingExample.http b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/TestCustomBindingExample.http new file mode 100644 index 000000000000..ee106316a2a2 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/TestCustomBindingExample.http @@ -0,0 +1,24 @@ +@host = https://localhost:7176 + +### Test the root endpoint +GET {{host}}/ +Accept: text/plain + +### Test the custom binding endpoint +GET {{host}}/custom-binding +X-Custom-Header: TestHeaderValue +Accept: text/plain + +### Test the combined parameter endpoint +GET {{host}}/combined/42 +X-Custom-Header: TestHeaderValue +Accept: text/plain + +### Test the validated parameter endpoint - valid case +GET {{host}}/validated +X-Custom-Header: ValidValue +Accept: text/plain + +### Test the validated parameter endpoint - invalid case +GET {{host}}/validated +Accept: text/plain \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/ValidatedParameters.cs b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/ValidatedParameters.cs new file mode 100644 index 000000000000..4a048b9c0a46 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/ValidatedParameters.cs @@ -0,0 +1,29 @@ +using System.Reflection; + +namespace CustomBindingExample; + +public class ValidatedParameter : IBindableFromHttpContext +{ + public string Value { get; init; } = default!; + + public static ValueTask BindAsync(HttpContext context, ParameterInfo parameter) + { + // Get the value from a query parameter + var value = context.Request.Query["value"].ToString(); + + // Perform some basic validation here + if (string.IsNullOrEmpty(value)) + { + // Return an empty instance - the controller action will handle the validation failure + return ValueTask.FromResult(new ValidatedParameter + { + Value = string.Empty + }); + } + + return ValueTask.FromResult(new ValidatedParameter + { + Value = value + }); + } +} \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/appsettings.Development.json b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/appsettings.Development.json new file mode 100644 index 000000000000..0c208ae9181e --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/appsettings.json b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/appsettings.json new file mode 100644 index 000000000000..ec04bc120fb7 --- /dev/null +++ b/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} \ No newline at end of file diff --git a/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding10.md b/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding10.md index 57e0fd53a662..1f4ac48e4336 100644 --- a/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding10.md +++ b/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding10.md @@ -321,10 +321,11 @@ The [complete sample code](https://github.com/dotnet/AspNetCore.Docs.Samples/tre ### Custom Binding -There are two ways to customize parameter binding: +There are three ways to customize parameter binding: 1. For route, query, and header binding sources, bind custom types by adding a static `TryParse` method for the type. 1. Control the binding process by implementing a `BindAsync` method on a type. +1. For advanced scenarios, implement the interface to provide custom binding logic directly from the `HttpContext`. #### TryParse @@ -354,6 +355,32 @@ The following code displays `SortBy:xyz, SortDirection:Desc, CurrentPage:99` wit +#### Custom parameter binding with `IBindableFromHttpContext` + +ASP.NET Core provides support for custom parameter binding in Minimal APIs using the interface. This interface, introduced with C# 11's static abstract members, allows you to create types that can be bound from an HTTP context directly in route handler parameters. + +```csharp +public interface IBindableFromHttpContext + where TSelf : class, IBindableFromHttpContext +{ + static abstract ValueTask BindAsync(HttpContext context, ParameterInfo parameter); +} +``` + +By implementing the , you can create custom types that handle their own binding logic from the HttpContext. When a route handler includes a parameter of this type, the framework automatically calls the static BindAsync method to create the instance: + +:::code language="csharp" source="~/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/Program.cs" id="snippet_IBindableFromHttpContext"::: + +The following is an example implementation of a custom parameter that binds from an HTTP header: + +:::code language="csharp" source="~/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/CustomBoundParameters.cs"::: + +You can also implement validation within your custom binding logic: + +:::code language="csharp" source="~/fundamentals/minimal-apis/10.0-samples/CustomBindingExample/Program.cs" id="snippet_Validation"::: + +[View or download the sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/minimal-apis/10.0-samples/CustomBindingExample) ([how to download](xref:index#how-to-download-a-sample)) + ### Binding failures When binding fails, the framework logs a debug message and returns various status codes to the client depending on the failure mode. diff --git a/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding7.md b/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding7.md index 54b284265abd..d78b27f5c435 100644 --- a/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding7.md +++ b/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding7.md @@ -249,10 +249,11 @@ The [complete sample code](https://github.com/dotnet/AspNetCore.Docs.Samples/tre ### Custom Binding -There are two ways to customize parameter binding: +There are three ways to customize parameter binding: 1. For route, query, and header binding sources, bind custom types by adding a static `TryParse` method for the type. 1. Control the binding process by implementing a `BindAsync` method on a type. +1. For advanced scenarios, implement the interface to provide custom binding logic directly from the `HttpContext`. #### TryParse @@ -282,6 +283,32 @@ The following code displays `SortBy:xyz, SortDirection:Desc, CurrentPage:99` wit +#### Custom parameter binding with `IBindableFromHttpContext` + +ASP.NET Core provides support for custom parameter binding in Minimal APIs using the interface. This interface, introduced with C# 11's static abstract members, allows you to create types that can be bound from an HTTP context directly in route handler parameters. + +```csharp +public interface IBindableFromHttpContext + where TSelf : class, IBindableFromHttpContext +{ + static abstract ValueTask BindAsync(HttpContext context, ParameterInfo parameter); +} +``` + +By implementing the , you can create custom types that handle their own binding logic from the HttpContext. When a route handler includes a parameter of this type, the framework automatically calls the static BindAsync method to create the instance: + +:::code language="csharp" source="~/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/Program.cs" id="snippet_IBindableFromHttpContext"::: + +The following is an example implementation of a custom parameter that binds from an HTTP header: + +:::code language="csharp" source="~/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/CustomBoundParameters.cs"::: + +You can also implement validation within your custom binding logic: + +:::code language="csharp" source="~/fundamentals/minimal-apis/7.0-samples/CustomBindingExample/Program.cs" id="snippet_Validation"::: + +[View or download the sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/minimal-apis/7.0-samples/CustomBindingExample) ([how to download](xref:index#how-to-download-a-sample)) + ### Binding failures When binding fails, the framework logs a debug message and returns various status codes to the client depending on the failure mode. diff --git a/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding8.md b/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding8.md index 841f36a6c64e..0c3fb760027f 100644 --- a/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding8.md +++ b/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding8.md @@ -321,10 +321,11 @@ The [complete sample code](https://github.com/dotnet/AspNetCore.Docs.Samples/tre ### Custom Binding -There are two ways to customize parameter binding: +There are three ways to customize parameter binding: 1. For route, query, and header binding sources, bind custom types by adding a static `TryParse` method for the type. 1. Control the binding process by implementing a `BindAsync` method on a type. +1. For advanced scenarios, implement the interface to provide custom binding logic directly from the `HttpContext`. #### TryParse @@ -354,6 +355,32 @@ The following code displays `SortBy:xyz, SortDirection:Desc, CurrentPage:99` wit +#### Custom parameter binding with `IBindableFromHttpContext` + +ASP.NET Core provides support for custom parameter binding in Minimal APIs using the interface. This interface, introduced with C# 11's static abstract members, allows you to create types that can be bound from an HTTP context directly in route handler parameters. + +```csharp +public interface IBindableFromHttpContext + where TSelf : class, IBindableFromHttpContext +{ + static abstract ValueTask BindAsync(HttpContext context, ParameterInfo parameter); +} +``` + +By implementing the , you can create custom types that handle their own binding logic from the HttpContext. When a route handler includes a parameter of this type, the framework automatically calls the static BindAsync method to create the instance: + +:::code language="csharp" source="~/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/Program.cs" id="snippet_IBindableFromHttpContext"::: + +The following is an example implementation of a custom parameter that binds from an HTTP header: + +:::code language="csharp" source="~/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/CustomBoundParameters.cs"::: + +You can also implement validation within your custom binding logic: + +:::code language="csharp" source="~/fundamentals/minimal-apis/8.0-samples/CustomBindingExample/Program.cs" id="snippet_Validation"::: + +[View or download the sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/minimal-apis/8.0-samples/CustomBindingExample) ([how to download](xref:index#how-to-download-a-sample)) + ### Binding failures When binding fails, the framework logs a debug message and returns various status codes to the client depending on the failure mode. diff --git a/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding9.md b/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding9.md index b946ee939e75..55a11acd2633 100644 --- a/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding9.md +++ b/aspnetcore/fundamentals/minimal-apis/includes/parameter-binding9.md @@ -321,10 +321,11 @@ The [complete sample code](https://github.com/dotnet/AspNetCore.Docs.Samples/tre ### Custom Binding -There are two ways to customize parameter binding: +There are three ways to customize parameter binding: 1. For route, query, and header binding sources, bind custom types by adding a static `TryParse` method for the type. 1. Control the binding process by implementing a `BindAsync` method on a type. +1. For advanced scenarios, implement the interface to provide custom binding logic directly from the `HttpContext`. #### TryParse @@ -354,6 +355,32 @@ The following code displays `SortBy:xyz, SortDirection:Desc, CurrentPage:99` wit +#### Custom parameter binding with `IBindableFromHttpContext` + +ASP.NET Core provides support for custom parameter binding in Minimal APIs using the interface. This interface, introduced with C# 11's static abstract members, allows you to create types that can be bound from an HTTP context directly in route handler parameters. + +```csharp +public interface IBindableFromHttpContext + where TSelf : class, IBindableFromHttpContext +{ + static abstract ValueTask BindAsync(HttpContext context, ParameterInfo parameter); +} +``` + +By implementing the , you can create custom types that handle their own binding logic from the HttpContext. When a route handler includes a parameter of this type, the framework automatically calls the static BindAsync method to create the instance: + +:::code language="csharp" source="~/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/Program.cs" id="snippet_IBindableFromHttpContext"::: + +The following is an example implementation of a custom parameter that binds from an HTTP header: + +:::code language="csharp" source="~/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/CustomBoundParameters.cs"::: + +You can also implement validation within your custom binding logic: + +:::code language="csharp" source="~/fundamentals/minimal-apis/9.0-samples/CustomBindingExample/Program.cs" id="snippet_Validation"::: + +[View or download the sample code](https://github.com/dotnet/AspNetCore.Docs/tree/main/aspnetcore/fundamentals/minimal-apis/9.0-samples/CustomBindingExample) ([how to download](xref:index#how-to-download-a-sample)) + ### Binding failures When binding fails, the framework logs a debug message and returns various status codes to the client depending on the failure mode. diff --git a/aspnetcore/fundamentals/minimal-apis/parameter-binding.md b/aspnetcore/fundamentals/minimal-apis/parameter-binding.md index 7f6ee4a329bc..55a79bd352a8 100644 --- a/aspnetcore/fundamentals/minimal-apis/parameter-binding.md +++ b/aspnetcore/fundamentals/minimal-apis/parameter-binding.md @@ -4,8 +4,9 @@ author: wadepickett description: Learn how parameters are populated before invoking minimal route handlers. ms.author: wpickett monikerRange: '>= aspnetcore-7.0' -ms.date: 07/09/2025 +ms.date: 09/02/2025 uid: fundamentals/minimal-apis/parameter-binding +ai-usage: ai-assisted --- # Parameter Binding in Minimal API apps