Skip to content

Instantly share code, notes, and snippets.

@skynode
Forked from davidfowl/MinimalAPIs.md
Created May 3, 2022 10:21
Show Gist options
  • Select an option

  • Save skynode/d8e89c8b8143c2022b4fc78c4db367db to your computer and use it in GitHub Desktop.

Select an option

Save skynode/d8e89c8b8143c2022b4fc78c4db367db to your computer and use it in GitHub Desktop.

Revisions

  1. @davidfowl davidfowl revised this gist Feb 20, 2022. 1 changed file with 2 additions and 0 deletions.
    2 changes: 2 additions & 0 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,7 @@
    # Minimal APIs at a glance

    [This document](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0) now exists on the official ASP.NET core docs page.

    - **Application**
    - [WebApplication](#webapplication)
    - [WebApplicationBuilder](#webapplicationbuilder)
  2. @davidfowl davidfowl revised this gist Jan 27, 2022. 1 changed file with 1 addition and 4 deletions.
    5 changes: 1 addition & 4 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -766,10 +766,7 @@ app.Run();
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    app.MapPost("/", (ClaimsPrincipal user) =>
    {
    return user.Identity.Name;
    });
    app.MapGet("/", (ClaimsPrincipal user) => user.Identity.Name);

    app.Run();
    ```
  3. @davidfowl davidfowl revised this gist Jan 27, 2022. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -733,7 +733,7 @@ There are some special types that the framework supports binding without any exp
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    app.MapPost("/", context => context.Response.WriteAsync("Hello World"));
    app.MapGet("/", (HttpContext context) => context.Response.WriteAsync("Hello World"));

    app.Run();
    ```
    @@ -743,7 +743,7 @@ app.Run();
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    app.MapPost("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));
    app.MapGet("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));

    app.Run();
    ```
    @@ -753,7 +753,7 @@ app.Run();
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    app.MapPost("/", async (CancellationToken cancellationToken) =>
    app.MapGet("/", async (CancellationToken cancellationToken) =>
    {
    await MakeLongRunningRequestAsync(cancellationToken)
    });
  4. @davidfowl davidfowl revised this gist Jan 26, 2022. 1 changed file with 46 additions and 0 deletions.
    46 changes: 46 additions & 0 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -728,6 +728,52 @@ There are some special types that the framework supports binding without any exp
    - `CancellationToken` - The cancellation token associated with the current http request.
    - `ClaimsPrincipal` - The user associated with the request (`HttpContext.User`).

    **HttpContext**

    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    app.MapPost("/", context => context.Response.WriteAsync("Hello World"));

    app.Run();
    ```

    **HttpRequest/HttpResponse**

    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    app.MapPost("/", (HttpRequest request, HttpResponse response) => response.WriteAsync($"Hello World {request.Query["name"]}"));

    app.Run();
    ```

    **CancellationToken**

    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    app.MapPost("/", async (CancellationToken cancellationToken) =>
    {
    await MakeLongRunningRequestAsync(cancellationToken)
    });

    app.Run();
    ```

    **ClaimsPrincipal**

    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    app.MapPost("/", (ClaimsPrincipal user) =>
    {
    return user.Identity.Name;
    });

    app.Run();
    ```

    ### Custom Binding

    There are 2 ways to customize parameter binding:
  5. @davidfowl davidfowl revised this gist Sep 21, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -551,7 +551,7 @@ Microsoft.AspNetCore.Http.BadHttpRequestException: Failed to bind parameter "int
    ### Wildcards/Catch all routes

    ```csharp
    app.MapGet("/posts/{rest*}", (string rest) => $"Routing to {rest}");
    app.MapGet("/posts/{*rest}", (string rest) => $"Routing to {rest}");
    ```

    ### Route constraints
  6. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 17 additions and 0 deletions.
    17 changes: 17 additions & 0 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -129,6 +129,23 @@ app.Run();

    ### HTTPS with custom certificate

    ***Using appsettings.json***

    ```json
    {
    "Kestrel" :{
    "Certificates": {
    "Default": {
    "Path": "cert.pem",
    "KeyPath": "key.pem"
    }
    }
    }
    }
    ```

    **Configuration via code**

    ```csharp
    var builder = WebApplication.CreateBuilder(args);

  7. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 29 additions and 2 deletions.
    31 changes: 29 additions & 2 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -133,8 +133,35 @@ app.Run();
    var builder = WebApplication.CreateBuilder(args);

    // Configure the cert and the key
    builder.Configuration["Kestrel:Certificates:Default:Path"] = "site.crt";
    builder.Configuration["Kestrel:Certificates:Default:KeyPath"] = "site.key";
    builder.Configuration["Kestrel:Certificates:Default:Path"] = "cert.pem";
    builder.Configuration["Kestrel:Certificates:Default:KeyPath"] = "key.pem";

    var app = builder.Build();

    app.Urls.Add("https://localhost:3000");

    app.MapGet("/", () => "Hello World");

    app.Run();
    ```

    **Using the certificate APIs**

    ```csharp
    using System.Security.Cryptography.X509Certificates;

    var builder = WebApplication.CreateBuilder(args);

    builder.WebHost.ConfigureKestrel(options =>
    {
    options.ConfigureHttpsDefaults(httpsOptions =>
    {
    var certPath = Path.Combine(builder.Environment.ContentRootPath, "cert.pem");
    var keyPath = Path.Combine(builder.Environment.ContentRootPath, "key.pem");

    httpsOptions.ServerCertificate = X509Certificate2.CreateFromPemFile(certPath, keyPath);
    });
    });

    var app = builder.Build();

  8. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -1164,7 +1164,7 @@ app.MapGet("/upload", async (HttpRequest req) =>

    ## Building libraries for ASP.NET Core

    The existing .NET ecosystem has built extensibility around `IServiceCollection`, `IHostBuilder` and `IWebHostBuilder`. These properties are exposed on the `WebApplicationBuilder` as `Services`, `Host` and `WebHost`.
    The existing .NET ecosystem has built extensibility around `IServiceCollection`, `IHostBuilder` and `IWebHostBuilder`. These properties are available on the `WebApplicationBuilder` as `Services`, `Host` and `WebHost`.

    The `WebApplication` implements both `Microsoft.AspNetCore.Builder.IApplicationBuilder` and `Microsoft.AspNetCore.Routing.IEndpointRouteBuilder`.

  9. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 15 additions and 0 deletions.
    15 changes: 15 additions & 0 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -1042,6 +1042,21 @@ app.MapGet("/admin", () => "This endpoint is for admins only")
    .RequireAuthorization("AdminsOnly");
    ```

    ### Allowing unauthenticated users to an endpoint

    It's also possible to allow any unauthenticated users to endpoints by using the `AllowAnonymous` attribute or
    the `AllowAnonymous` method.

    ```csharp
    app.MapGet("/login", [AllowAnonymous] () => "This endpoint is for admins only");
    ```


    ```csharp
    app.MapGet("/login", () => "This endpoint is for admins only")
    .AllowAnonymous();
    ```

    ## CORS

    Routes can be CORS enabled using CORS policies. These can be declared via the `EnableCors` attribute or by using the
  10. @davidfowl davidfowl revised this gist Sep 16, 2021. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -358,7 +358,7 @@ The WebApplication has a the developer exception enabled by default when the env
    ```csharp
    var app = WebApplication.Create(args);

    app.MapGet("/", () => { throw new InvalidOperationException(); });
    app.MapGet("/", () => throw new InvalidOperationException("Oops, the '/' route has thrown an exception."));

    app.Run();
    ```
    @@ -615,10 +615,10 @@ Attributes can be used to explicitly declare where parameters should be bound fr
    ```csharp
    using Microsoft.AspNetCore.Mvc;

    app.MapGet("/{id}", ([FromRoute]int id,
    [FromQuery(Name = "p")]int page,
    [FromServices]Service service,
    [FromHeader(Name = "Content-Type")]string contentType) => { });
    app.MapGet("/{id}", ([FromRoute] int id,
    [FromQuery(Name = "p")] int page,
    [FromServices] Service service,
    [FromHeader(Name = "Content-Type")] string contentType) => { });
    ```

    |Parameter| Binding Source|
  11. @davidfowl davidfowl revised this gist Sep 12, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -703,7 +703,7 @@ public static bool TryParse(string value, IFormatProvider provider, T out result
    **Example**

    ```csharp
    app.MapGet("/map/{point}", (Point point) => $"Point: {point.X}, {point.Y}");
    app.MapGet("/map", (Point point) => $"Point: {point.X}, {point.Y}");

    public class Point
    {
  12. @davidfowl davidfowl revised this gist Sep 12, 2021. 1 changed file with 3 additions and 1 deletion.
    4 changes: 3 additions & 1 deletion MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -26,6 +26,8 @@ app.MapGet("/", () => "Hello World");
    app.Run();
    ```

    This listens to port http://localhost:5000 and https://localhost:5001 by default.

    ### Changing the port

    ```csharp
    @@ -727,7 +729,7 @@ public class Point
    }
    ```

    A request to `/point?point=(12.3,10.1)` returns:
    A request to `/map?point=(12.3,10.1)` returns:

    ```
    Point: 12.3,10.1
  13. @davidfowl davidfowl revised this gist Sep 12, 2021. 1 changed file with 0 additions and 2 deletions.
    2 changes: 0 additions & 2 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -26,8 +26,6 @@ app.MapGet("/", () => "Hello World");
    app.Run();
    ```

    This listens to port http://localhost:5000 and https://localhost:5001 by default.

    ### Changing the port

    ```csharp
  14. @davidfowl davidfowl revised this gist Sep 12, 2021. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -1163,4 +1163,7 @@ We expect library authors to continue targeting `IHostBuilder`, `IWebHostBuilder
    - No support for binding from forms. This includes binding `IFormFile` (this will be added in the future).
    - No built-in support for validation. i.e. `IModelValidator`
    - No support for application parts or the application model. There's no way to apply or build your own conventions.
    - No built-in view rendering support. We recommend using Razor Pages for rendering views.
    - No built-in view rendering support. We recommend using Razor Pages for rendering views.
    - No support for [JsonPatch](https://www.nuget.org/packages/Microsoft.AspNetCore.JsonPatch/)
    - No support for [OData](https://www.nuget.org/packages/Microsoft.AspNetCore.OData/)
    - No support for [ApiVersioning](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Versioning/), see [this issue](https://github.com/dotnet/aspnet-api-versioning/issues/751) for more details.
  15. @davidfowl davidfowl revised this gist Sep 12, 2021. 1 changed file with 46 additions and 8 deletions.
    54 changes: 46 additions & 8 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -1,14 +1,18 @@
    # Minimal APIs at a glance

    - [WebApplication](#webapplication)
    - [WebApplicationBuilder](#webapplicationbuilder)
    - [Routing](#routing)
    - [Parameter Binding](#parameter-binding)
    - [Responses](#responses)
    - **Application**
    - [WebApplication](#webapplication)
    - [WebApplicationBuilder](#webapplicationbuilder)
    - **Request Handling**
    - [Routing](#routing)
    - [Parameter Binding](#parameter-binding)
    - [Responses](#responses)
    - [Authorization](#authorization)
    - [CORS](#cors)
    - [OpenAPI](#openapi)
    - [Building libraries for ASP.NET Core](#)
    - [Differences with ASP.NET Core MVC](#differences-with-asp.net-core-mvc)
    - **Advanced**
    - [Building libraries for ASP.NET Core](#building-libraries-for-aspnet-core)
    - [Differences with ASP.NET Core MVC](#differences-with-aspnet-core-mvc)

    ## WebApplication

    @@ -1002,7 +1006,17 @@ app.MapGet("/html", () => Results.Extensions.Html(@$"<!doctype html>

    ## Authorization

    Routes can be protected using authorization policies. These can be declared via the authorize attribute or by using the `RequireAuthorization` method call.
    Routes can be protected using authorization policies. These can be declared via the `Authorize` attribute or by using the `RequireAuthorization` method.

    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    builder.Services.AddAuthorization(o => o.AddPolicy("AdminsOnly", b => b.RequireClaim("admin", "true"));

    var app = builder.Build();

    app.UseAuthorization();
    ```

    ```csharp
    app.MapGet("/auth", [Authorize] () => "This endpoint requires authorization");
    @@ -1028,6 +1042,30 @@ app.MapGet("/admin", () => "This endpoint is for admins only")
    .RequireAuthorization("AdminsOnly");
    ```

    ## CORS

    Routes can be CORS enabled using CORS policies. These can be declared via the `EnableCors` attribute or by using the
    `RequireCors` method.

    ```csharp
    var builder = WebApplication.CreateBuilder(args);
    builder.Services.AddCors(options => options.AddPolicy("AnyOrigin", o => o.AllowAnyOrigin()));

    var app = builder.Build();
    app.UseCors();
    ```

    ```csharp
    app.MapGet("/cors", [EnableCors("AnyOrigin")] () => "This endpoint allows cross origin requests!");
    ```

    OR

    ```csharp
    app.MapGet("/cors", () => "This endpoint allows cross origin requests!")
    .RequireCors("AnyOrigin");
    ```

    ## OpenAPI

    It's possible to describe the OpenAPI specification for route handlers using [Swashbuckle](https://www.nuget.org/packages/Swashbuckle.AspNetCore/).
  16. @davidfowl davidfowl revised this gist Sep 12, 2021. 1 changed file with 13 additions and 3 deletions.
    16 changes: 13 additions & 3 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,14 @@
    # Minimal APIs
    # Minimal APIs at a glance

    - [WebApplication](#webapplication)
    - [WebApplicationBuilder](#webapplicationbuilder)
    - [Routing](#routing)
    - [Parameter Binding](#parameter-binding)
    - [Responses](#responses)
    - [Authorization](#authorization)
    - [OpenAPI](#openapi)
    - [Building libraries for ASP.NET Core](#)
    - [Differences with ASP.NET Core MVC](#differences-with-asp.net-core-mvc)

    ## WebApplication

    @@ -513,7 +523,7 @@ app.MapGet("/posts/{slug:regex(^[a-z0-9_-]+$)}", (string slug) => $"Post {slug}"
    |`/posts/mypost`| `/posts/{slug:regex(^[a-z0-9_-]+$)}` |
    |`/posts/%`| No match |

    ### Built in contraints
    ### Contraints

    | constraint | Example | Example Matches | Notes |
    | ---------- | ------- | --------------- | ----- |
    @@ -1018,7 +1028,7 @@ app.MapGet("/admin", () => "This endpoint is for admins only")
    .RequireAuthorization("AdminsOnly");
    ```

    ## Open API/Swagger
    ## OpenAPI

    It's possible to describe the OpenAPI specification for route handlers using [Swashbuckle](https://www.nuget.org/packages/Swashbuckle.AspNetCore/).

  17. @davidfowl davidfowl revised this gist Sep 12, 2021. 1 changed file with 52 additions and 0 deletions.
    52 changes: 52 additions & 0 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -49,6 +49,58 @@ app.MapGet("/", () => "Hello World");
    app.Run($"http://localhost:{port}");
    ```

    ### Changing the ports via environment variables

    You can set the `ASPNETCORE_URLS` environment variable to change the address:

    ```
    ASPNETCORE_URLS=http://localhost:3000
    ```

    This supports multiple urls:

    ```
    ASPNETCORE_URLS=http://localhost:3000;https://localhost:5000
    ```

    ### Listening on all interfaces

    ```csharp
    var app = WebApplication.Create(args);

    app.Urls.Add("http://*:3000");

    app.MapGet("/", () => "Hello World");

    app.Run();
    ```

    ```csharp
    var app = WebApplication.Create(args);

    app.Urls.Add("http://+:3000");

    app.MapGet("/", () => "Hello World");

    app.Run();
    ```

    ```csharp
    var app = WebApplication.Create(args);

    app.Urls.Add("http://0.0.0.0:3000");

    app.MapGet("/", () => "Hello World");

    app.Run();
    ```

    This syntax also works in the environment variables:

    ```
    ASPNETCORE_URLS=http://*:3000;https://+:5000;http://0.0.0.0:5005
    ```

    ### HTTPS with development certificate

    ```csharp
  18. @davidfowl davidfowl revised this gist Sep 12, 2021. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -496,6 +496,8 @@ Supported binding sources:
    - Services provided by dependency injection
    - Custom

    **NOTE: Binding from forms are not natively supported in this release.**

    ### GET, HEAD, OPTIONS, DELETE

    The HTTP methods GET, HEAD, OPTIONS, DELETE will never bind from body. All other binding sources are supported.
    @@ -1058,6 +1060,7 @@ We expect library authors to continue targeting `IHostBuilder`, `IWebHostBuilder

    - No support for filters. i.e. `IAsyncAuthorizationFilter`, `IAsyncActionFilter`, `IAsyncExceptionFilter`, `IAsyncResultFilter`, `IAsyncResourceFilter`
    - No support for model binding. i.e. `IModelBinderProvider`, `IModelBinder`. Support can be added with a custom binding shim.
    - No support for binding from forms. This includes binding `IFormFile` (this will be added in the future).
    - No built-in support for validation. i.e. `IModelValidator`
    - No support for application parts or the application model. There's no way to apply or build your own conventions.
    - No built-in view rendering support. We recommend using Razor Pages for rendering views.
  19. @davidfowl davidfowl revised this gist Sep 12, 2021. 1 changed file with 9 additions and 3 deletions.
    12 changes: 9 additions & 3 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -301,8 +301,6 @@ Navigating to `/` will render a friendly page that shows the exception.

    ### ASP.NET Core Middleware

    This is a list of commonly used ASP.NET Core middleware.

    |Middleware |Description|API|
    |--|--|--|
    |Authentication|Provides authentication support. | `app.UseAuthentication()`
    @@ -314,7 +312,6 @@ This is a list of commonly used ASP.NET Core middleware.
    |HTTP Strict Transport Security (HSTS)|Security enhancement middleware that adds a special response header. | `app.UseHsts()`
    |Request Logging|Provides support for logging HTTP requests and responses. | `app.UseHttpLogging()`
    |W3C Request Logging|Provides support for logging HTTP requests and responses in the [W3C format](https://www.w3.org/TR/WD-logfile.html). | `app.UseW3CLogging()`
    |Rewrite |Provides support for rewriting the request. Supports PHP style mod_rewrite and IIS url rewrite rules. | `app.UseRewriter()`
    |Response Caching|Provides support for caching responses. | `app.UseResponseCaching()`
    |Response Compression|Provides support for compressing responses. | `app.UseResponseCompression()`
    |Session|Provides support for managing user sessions. | `app.UseSession()`
    @@ -1055,3 +1052,12 @@ The existing .NET ecosystem has built extensibility around `IServiceCollection`,
    The `WebApplication` implements both `Microsoft.AspNetCore.Builder.IApplicationBuilder` and `Microsoft.AspNetCore.Routing.IEndpointRouteBuilder`.

    We expect library authors to continue targeting `IHostBuilder`, `IWebHostBuilder`, `IApplicationBuilder` and `IEndpointRouteBuilder` when building ASP.NET Core specific components. This will ensure that your middleware, route handler, or other extensibility points continue to work across different hosting models.


    ## Differences with ASP.NET Core MVC

    - No support for filters. i.e. `IAsyncAuthorizationFilter`, `IAsyncActionFilter`, `IAsyncExceptionFilter`, `IAsyncResultFilter`, `IAsyncResourceFilter`
    - No support for model binding. i.e. `IModelBinderProvider`, `IModelBinder`. Support can be added with a custom binding shim.
    - No built-in support for validation. i.e. `IModelValidator`
    - No support for application parts or the application model. There's no way to apply or build your own conventions.
    - No built-in view rendering support. We recommend using Razor Pages for rendering views.
  20. @davidfowl davidfowl revised this gist Sep 12, 2021. 1 changed file with 3 additions and 0 deletions.
    3 changes: 3 additions & 0 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -301,6 +301,8 @@ Navigating to `/` will render a friendly page that shows the exception.

    ### ASP.NET Core Middleware

    This is a list of commonly used ASP.NET Core middleware.

    |Middleware |Description|API|
    |--|--|--|
    |Authentication|Provides authentication support. | `app.UseAuthentication()`
    @@ -312,6 +314,7 @@ Navigating to `/` will render a friendly page that shows the exception.
    |HTTP Strict Transport Security (HSTS)|Security enhancement middleware that adds a special response header. | `app.UseHsts()`
    |Request Logging|Provides support for logging HTTP requests and responses. | `app.UseHttpLogging()`
    |W3C Request Logging|Provides support for logging HTTP requests and responses in the [W3C format](https://www.w3.org/TR/WD-logfile.html). | `app.UseW3CLogging()`
    |Rewrite |Provides support for rewriting the request. Supports PHP style mod_rewrite and IIS url rewrite rules. | `app.UseRewriter()`
    |Response Caching|Provides support for caching responses. | `app.UseResponseCaching()`
    |Response Compression|Provides support for compressing responses. | `app.UseResponseCompression()`
    |Session|Provides support for managing user sessions. | `app.UseSession()`
  21. @davidfowl davidfowl revised this gist Sep 11, 2021. 1 changed file with 67 additions and 67 deletions.
    134 changes: 67 additions & 67 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@

    ### Creating an application

    ```C#
    ```csharp
    var app = WebApplication.Create(args);

    app.MapGet("/", () => "Hello World");
    @@ -16,7 +16,7 @@ This listens to port http://localhost:5000 and https://localhost:5001 by default

    ### Changing the port

    ```C#
    ```csharp
    var app = WebApplication.Create(args);

    app.MapGet("/", () => "Hello World");
    @@ -26,7 +26,7 @@ app.Run("http://localhost:3000");

    ### Multiple ports

    ```C#
    ```csharp
    var app = WebApplication.Create(args);

    app.Urls.Add("http://localhost:3000");
    @@ -39,7 +39,7 @@ app.Run();

    ### Reading the port from environment

    ```C#
    ```csharp
    var app = WebApplication.Create(args);

    var port = Environment.GetEnvironmentVariable("PORT") ?? "3000";
    @@ -51,7 +51,7 @@ app.Run($"http://localhost:{port}");

    ### HTTPS with development certificate

    ```C#
    ```csharp
    var app = WebApplication.Create(args);

    app.Urls.Add("https://localhost:3000");
    @@ -63,7 +63,7 @@ app.Run();

    ### HTTPS with custom certificate

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    // Configure the cert and the key
    @@ -81,7 +81,7 @@ app.Run();

    ### Reading the environment

    ```C#
    ```csharp
    var app = WebApplication.Create(args);

    if (!app.Environment.IsDevelopment())
    @@ -97,7 +97,7 @@ app.Run();

    ### Reading configuration

    ```C#
    ```csharp
    var app = WebApplication.Create(args);

    Console.WriteLine($"The configuration value is {app.Configuration["key"]}");
    @@ -107,7 +107,7 @@ app.Run();

    ### Logging

    ```C#
    ```csharp
    var app = WebApplication.Create(args);

    app.Logger.LogInformation("The application started");
    @@ -119,7 +119,7 @@ app.Run();

    ### Changing the content root, application name and environment

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(new WebApplicationOptions
    {
    ApplicationName = typeof(Program).Assembly.FullName,
    @@ -150,7 +150,7 @@ OR via command line arguments:

    ### Adding configuration providers

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    builder.Configuration.AddIniFile("appsettings.ini");
    @@ -166,7 +166,7 @@ By default the `WebApplicationBuilder` reads configuration from:
    - environment variables
    - The command line

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    // Reads the ConnectionStrings section of configuration and looks for a sub key called Todos
    @@ -179,7 +179,7 @@ var app = builder.Build();

    ### Reading the environment

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    if (builder.Environment.IsDevelopment())
    @@ -192,7 +192,7 @@ var app = builder.Build();

    ### Adding logging providers

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    // Configure JSON logging to the console
    @@ -203,7 +203,7 @@ var app = builder.Build();

    ### Adding services

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    // Add the memory cache services
    @@ -219,7 +219,7 @@ var app = builder.Build();

    Existing extension methods on `IHostBuilder` can be accessed using the `Host` property.

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    // Wait 30 seconds for graceful shutdown
    @@ -232,7 +232,7 @@ var app = builder.Build();

    Existing extension methods on `IWebHostBuilder` can be accessed using the `WebHost` property.

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    // Change the HTTP server implemenation to be HTTP.sys based
    @@ -246,7 +246,7 @@ var app = builder.Build();
    By default, the web root is relative to the content root in the `wwwroot` folder. This is where the static files
    middleware expects to find static files. You can change this by using the `UseWebRoot` method on the `WebHost` property:

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    // Look for static files in webroot
    @@ -259,7 +259,7 @@ var app = builder.Build();

    This example uses [Autofac](https://autofac.readthedocs.io/en/latest/integration/aspnetcore.html)

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
    @@ -276,7 +276,7 @@ var app = builder.Build();

    Any existing ASP.NET Core middleware can be configured on the WebApplication:

    ```C#
    ```csharp
    var app = WebApplication.Create(args);

    // Setup the file server to serve static files
    @@ -289,7 +289,7 @@ app.Run();

    The WebApplication has a the developer exception enabled by default when the environment is development:

    ```C#
    ```csharp
    var app = WebApplication.Create(args);

    app.MapGet("/", () => { throw new InvalidOperationException(); });
    @@ -322,7 +322,7 @@ Navigating to `/` will render a friendly page that shows the exception.

    You can route to handlers using the various Map* methods on `WebApplication`. There's a `Map{HTTPMethod}` method to allow handling different HTTP methods:

    ```C#
    ```csharp
    app.MapGet("/", () => "This is a GET");
    app.MapPost("/", () => "This is a POST");
    app.MapPut("/", () => "This is a PUT");
    @@ -331,7 +331,7 @@ app.MapDelete("/", () => "This is a DELETE");

    Other HTTP methods:

    ```C#
    ```csharp
    app.MapMethods("/options-or-head", new [] { "OPTIONS", "HEAD" }, () => "This is an options or head request ");
    ```

    @@ -342,7 +342,7 @@ an instance method or a static method.

    **Lambda expression**

    ```C#
    ```csharp
    app.MapGet("/", () => "This is an inline lambda");

    var handler = () => "This is a lambda variable";
    @@ -352,15 +352,15 @@ app.MapGet("/", handler);

    **Local function**

    ```C#
    ```csharp
    string LocalFunction() => "This is local function"

    app.MapGet("/", LocalFunction);
    ```

    **Instance method**

    ```C#
    ```csharp
    var handler = new HelloHandler();

    app.MapGet("/", handler.Hello);
    @@ -376,7 +376,7 @@ class HelloHandler

    **Static method**

    ```C#
    ```csharp
    app.MapGet("/", HelloHandler.Hello);

    class HelloHandler
    @@ -394,7 +394,7 @@ This provides the appropriate flexiblity when deciding how to organize your rout

    Routes can be given names in order to generate URLs to them. This avoids having to hard code paths in your application.

    ```C#
    ```csharp
    app.MapGet("/hello", () => "Hello there")
    .WithName("hi");

    @@ -403,7 +403,7 @@ app.MapGet("/", (LinkGenerator linker) => $"The link to the hello route is {link

    Route names are inferred from method names if specified:

    ```C#
    ```csharp
    string Hi() => "Hello there";

    app.MapGet("/hello", Hi);
    @@ -419,7 +419,7 @@ These names must be globally unique and are also used as the OpenAPI operation i

    You can capture route parameters as part of the route pattern definition:

    ```C#
    ```csharp
    app.MapGet("/users/{userId}/books/{bookId}", (int userId, int bookId) => $"The user id is {userId} and book id is {bookId}");
    ```

    @@ -440,15 +440,15 @@ Microsoft.AspNetCore.Http.BadHttpRequestException: Failed to bind parameter "int

    ### Wildcards/Catch all routes

    ```C#
    ```csharp
    app.MapGet("/posts/{rest*}", (string rest) => $"Routing to {rest}");
    ```

    ### Route constraints

    Route constraints are influence the matching behavior of a route.

    ```C#
    ```csharp
    app.MapGet("/todos/{id:int}", (int id) => db.Todos.Find(id));
    app.MapGet("/todos/{text}", (string text) => db.Todos.Where(t => t.Text.Contains(text));
    app.MapGet("/posts/{slug:regex(^[a-z0-9_-]+$)}", (string slug) => $"Post {slug}");
    @@ -502,7 +502,7 @@ The HTTP methods GET, HEAD, OPTIONS, DELETE will never bind from body. All other

    **NOTE: If you need to support the case where you have a GET with a body, you can directly read it from the HttpRequest.**

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    // Added as service
    @@ -522,7 +522,7 @@ class Service { }

    ### Other verbs (POST, PUT, PATCH, etc)

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    // Added as service
    @@ -544,7 +544,7 @@ record Person(string Name, int Age);

    Attributes can be used to explicitly declare where parameters should be bound from.

    ```C#
    ```csharp
    using Microsoft.AspNetCore.Mvc;

    app.MapGet("/{id}", ([FromRoute]int id,
    @@ -566,19 +566,19 @@ Binding from form values is not supported at this time.

    Parameters declared in route handlers will be treated as required. This means if a request matches the route, the route handler will only execute if all required paramters are provided in the request. Failure to do so will result in an error.

    ```C#
    ```csharp
    app.MapGet("/products", (int pageNumber) => $"Requesting page {pageNumber}");
    ```

    Since the `pageNumber` paramter is required, this route handler won't execute if the query string `pageNumber` isn't provided. To make it optional define the type as nullable.

    ```C#
    ```csharp
    app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
    ```

    This also works with methods that have a default value:

    ```C#
    ```csharp
    string ListProducts(int pageNumber = 1) => $"Requesting page {pageNumber}";

    app.MapGet("/products", ListProducts);
    @@ -588,15 +588,15 @@ The above will default to 1 if the pageNumber isn't specified in the query strin

    This logic applies to all sources.

    ```C#
    ```csharp
    app.MapPost("/products", (Product? product) => () => { });
    ```

    The above will call the method with a null product if no request body was sent.

    **NOTE: If invalid data is provided and the parameter is nullable, the route handler will not be executed.**

    ```C#
    ```csharp
    app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
    ```

    @@ -627,14 +627,14 @@ There are 2 ways to customize parameter binding:

    The TryParse method must be of the form(s):

    ```C#
    ```csharp
    public static bool TryParse(string value, T out result);
    public static bool TryParse(string value, IFormatProvider provider, T out result);
    ```

    **Example**

    ```C#
    ```csharp
    app.MapGet("/map/{point}", (Point point) => $"Point: {point.X}, {point.Y}");

    public class Point
    @@ -671,13 +671,13 @@ Point: 12.3,10.1

    The `BindAsync` method must be of the form:

    ```C#
    ```csharp
    public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo parameter);
    ```

    **Example**

    ```C#
    ```csharp
    app.MapGet("/products", (PagingData pageData) => $"SortBy:{pageData.SortBy}, SortDirection:{pageData.SortDirection}, CurrentPage:{pageData.CurrentPage}");

    public class PagingData
    @@ -757,7 +757,7 @@ The body binding source uses System.Text.Json for de-serialization. It is *NOT*
    the binding using other techniques described in above sections. To customize JSON serializer options, you can use
    the following:

    ```C#
    ```csharp
    using Microsoft.AspNetCore.Http.Json;

    var builder = WebApplication.CreateBuilder(args);
    @@ -800,25 +800,25 @@ Route handlers support 2 types of return values:

    **Example: string return values**

    ```C#
    ```csharp
    app.MapGet("/hello", () => "Hello World");
    ```

    **Example: JSON return values**

    ```C#
    ```csharp
    app.MapGet("/hello", () => new { Message = "Hello World" });
    ```

    **Example: IResult return values**

    ```C#
    ```csharp
    app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
    ```

    The following example uses the built-in result types to customize the response:

    ```C#
    ```csharp
    app.MapGet("/todos/{id}", (int id, TodoDb db) =>
    db.Todos.Find(id) is Todo todo
    ? Results.Ok(todo)
    @@ -828,25 +828,25 @@ app.MapGet("/todos/{id}", (int id, TodoDb db) =>

    ### JSON

    ```C#
    ```csharp
    app.MapGet("/hello", () => Results.Json(new { Message = "Hello World" }));
    ```

    ### Custom Status Code

    ```C#
    ```csharp
    app.MapGet("/405", () => Results.StatusCode(405));
    ```

    ### Text

    ```C#
    ```csharp
    app.MapGet("/text", () => Results.Text("This is some text"));
    ```

    ### Stream

    ```C#
    ```csharp
    var proxyClient = new HttpClient();
    app.MapGet("/pokemon", async () =>
    {
    @@ -858,13 +858,13 @@ app.MapGet("/pokemon", async () =>

    ### Redirect

    ```C#
    ```csharp
    app.MapGet("/old-path", () => Results.Redirect("/new-path"));
    ```

    ### File

    ```C#
    ```csharp
    app.MapGet("/download", () => Results.File("foo.text"));
    ```

    @@ -892,7 +892,7 @@ Write a JSON response with advanced options |application/json |200|`Results.Json

    Users can take control of responses by implementing a custom `IResult` type. Here's an example of an HTML result type:

    ```C#
    ```csharp
    namespace Microsoft.AspNetCore.Http;

    static class ResultsExtensions
    @@ -925,7 +925,7 @@ class HtmlResult : IResult

    We recommend adding an extension method to `Microsoft.AspNetCore.Http.IResultExtensions` to make these custom results more discoverable.

    ```C#
    ```csharp
    app.MapGet("/html", () => Results.Extensions.Html(@$"<!doctype html>
    <html>
    <head><title>miniHTML</title></head>
    @@ -940,26 +940,26 @@ app.MapGet("/html", () => Results.Extensions.Html(@$"<!doctype html>

    Routes can be protected using authorization policies. These can be declared via the authorize attribute or by using the `RequireAuthorization` method call.

    ```C#
    ```csharp
    app.MapGet("/auth", [Authorize] () => "This endpoint requires authorization");
    ```

    OR

    ```C#
    ```csharp
    app.MapGet("/auth", () => "This endpoint requires authorization")
    .RequireAuthorization();
    ```

    Authorization policies can be configured as well:

    ```C#
    ```csharp
    app.MapGet("/admin", [Authorize("AdminsOnly")] () => "This endpoint is for admins only");
    ```

    OR

    ```C#
    ```csharp
    app.MapGet("/admin", () => "This endpoint is for admins only")
    .RequireAuthorization("AdminsOnly");
    ```
    @@ -970,7 +970,7 @@ It's possible to describe the OpenAPI specification for route handlers using [Sw

    Below is an example of a typical ASP.NET Core application with OpenAPI suppport:

    ```C#
    ```csharp
    var builder = WebApplication.CreateBuilder(args);

    builder.Services.AddEndpointsApiExplorer();
    @@ -990,36 +990,36 @@ if (app.Environment.IsDevelopment())

    ### Describing response types

    ```C#
    ```csharp
    app.MapGet("/api/products", (int id, ProductDb db) => db.Products.Find(id) is Product product ? Results.Ok(product) : Results.NotFound())
    .Produces<Product>(200)
    .Produces(404);
    ```

    ### Exclude Open API description

    ```C#
    ```csharp
    app.MapGet("/skipme", () => { })
    .ExcludeFromDescription();
    ```

    ### Add operation ids to Open API

    ```C#
    ```csharp
    app.MapGet("/api/products", (ProductDb db) => db.Products.ToListAsync())
    .WithName("GetProducts");
    ```

    ### Add tags to the Open API description (used for grouping)

    ```C#
    ```csharp
    app.MapGet("/api/products", (ProductDb db) => db.Products.ToListAsync())
    .WithTags("ProductsGroup");
    ```

    ### Describe request body

    ```C#
    ```csharp
    app.MapGet("/upload", async (HttpRequest req) =>
    {
    if (!req.HasFormContentType)
  22. @davidfowl davidfowl revised this gist Sep 11, 2021. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -353,7 +353,7 @@ app.MapGet("/", handler);
    **Local function**

    ```C#
    void LocalFunction() => "This is local function"
    string LocalFunction() => "This is local function"

    app.MapGet("/", LocalFunction);
    ```
    @@ -1036,8 +1036,8 @@ app.MapGet("/upload", async (HttpRequest req) =>
    }

    var uploads = Path.Combine(uploadsPath, file.FileName);
    using var fileStream = File.OpenWrite(uploads);
    using var uploadStream = file.OpenReadStream();
    await using var fileStream = File.OpenWrite(uploads);
    await using var uploadStream = file.OpenReadStream();
    await uploadStream.CopyToAsync(fileStream);

    return Results.NoContent();
  23. @davidfowl davidfowl revised this gist Sep 11, 2021. 1 changed file with 37 additions and 4 deletions.
    41 changes: 37 additions & 4 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -548,9 +548,9 @@ Attributes can be used to explicitly declare where parameters should be bound fr
    using Microsoft.AspNetCore.Mvc;

    app.MapGet("/{id}", ([FromRoute]int id,
    [FromQuery("p")]int page,
    [FromQuery(Name = "p")]int page,
    [FromServices]Service service,
    [FromHeader("Content-Type")]string contentType) => { });
    [FromHeader(Name = "Content-Type")]string contentType) => { });
    ```

    |Parameter| Binding Source|
    @@ -581,7 +581,7 @@ This also works with methods that have a default value:
    ```C#
    string ListProducts(int pageNumber = 1) => $"Requesting page {pageNumber}";

    app.MapGet("/products", (int? pageNumber) => ListProducts);
    app.MapGet("/products", ListProducts);
    ```

    The above will default to 1 if the pageNumber isn't specified in the query string.
    @@ -592,7 +592,7 @@ This logic applies to all sources.
    app.MapPost("/products", (Product? product) => () => { });
    ```

    The above will call the method will a null product if no request body was sent.
    The above will call the method with a null product if no request body was sent.

    **NOTE: If invalid data is provided and the parameter is nullable, the route handler will not be executed.**

    @@ -751,6 +751,39 @@ The rules for determining a binding source from a parameter are as follows:
    1. If the parameter type is a service provided by dependency injection, it will use that as the source.
    1. The parameter is from the body.

    ### Customizing JSON binding

    The body binding source uses System.Text.Json for de-serialization. It is *NOT* possible to change this default but you can customize
    the binding using other techniques described in above sections. To customize JSON serializer options, you can use
    the following:

    ```C#
    using Microsoft.AspNetCore.Http.Json;

    var builder = WebApplication.CreateBuilder(args);

    // Configure JSON options
    builder.Services.Configure<JsonOptions>(options =>
    {
    options.SerializerOptions.IncludeFields = true;
    });

    var app = builder.Build();

    app.MapPost("/products", (Product product) => product);

    app.Run();

    class Product
    {
    // These are public fields instead of properties
    public int Id;
    public string Name;
    }
    ```

    This configures both the input and output default JSON options.

    ## Responses

    Route handlers support 2 types of return values:
  24. @davidfowl davidfowl revised this gist Sep 11, 2021. 1 changed file with 25 additions and 0 deletions.
    25 changes: 25 additions & 0 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -390,6 +390,31 @@ class HelloHandler

    This provides the appropriate flexiblity when deciding how to organize your route handlers.

    ### Naming routes and link generation

    Routes can be given names in order to generate URLs to them. This avoids having to hard code paths in your application.

    ```C#
    app.MapGet("/hello", () => "Hello there")
    .WithName("hi");

    app.MapGet("/", (LinkGenerator linker) => $"The link to the hello route is {linker.GetPathByName("hi", values: null)}");
    ```

    Route names are inferred from method names if specified:

    ```C#
    string Hi() => "Hello there";

    app.MapGet("/hello", Hi);

    app.MapGet("/", (LinkGenerator linker) => $"The link to the hello route is {linker.GetPathByName("Hi", values: null)}");
    ```

    **NOTE: Route names are case sensitive!**

    These names must be globally unique and are also used as the OpenAPI operation id when OpenAPI support is enabled (see the OpenAPI/Swagger section for more details).

    ### Route Parameters

    You can capture route parameters as part of the route pattern definition:
  25. @davidfowl davidfowl revised this gist Sep 11, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -337,7 +337,7 @@ app.MapMethods("/options-or-head", new [] { "OPTIONS", "HEAD" }, () => "This is

    ### Route Handlers

    Route handlers are methods that execute when the a route matches. Route handlers can be a function or any shape. It can be a lambda expression, a local function,
    Route handlers are methods that execute when the a route matches. Route handlers can be a function or any shape (including synchronous or asynchronous). It can be a lambda expression, a local function,
    an instance method or a static method.

    **Lambda expression**
  26. @davidfowl davidfowl revised this gist Sep 11, 2021. 1 changed file with 55 additions and 0 deletions.
    55 changes: 55 additions & 0 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -335,6 +335,61 @@ Other HTTP methods:
    app.MapMethods("/options-or-head", new [] { "OPTIONS", "HEAD" }, () => "This is an options or head request ");
    ```

    ### Route Handlers

    Route handlers are methods that execute when the a route matches. Route handlers can be a function or any shape. It can be a lambda expression, a local function,
    an instance method or a static method.

    **Lambda expression**

    ```C#
    app.MapGet("/", () => "This is an inline lambda");

    var handler = () => "This is a lambda variable";

    app.MapGet("/", handler);
    ```

    **Local function**

    ```C#
    void LocalFunction() => "This is local function"

    app.MapGet("/", LocalFunction);
    ```

    **Instance method**

    ```C#
    var handler = new HelloHandler();

    app.MapGet("/", handler.Hello);

    class HelloHandler
    {
    public string Hello()
    {
    return "Hello World";
    }
    }
    ```

    **Static method**

    ```C#
    app.MapGet("/", HelloHandler.Hello);

    class HelloHandler
    {
    public static string Hello()
    {
    return "Hello World";
    }
    }
    ```

    This provides the appropriate flexiblity when deciding how to organize your route handlers.

    ### Route Parameters

    You can capture route parameters as part of the route pattern definition:
  27. @davidfowl davidfowl revised this gist Sep 11, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -332,7 +332,7 @@ app.MapDelete("/", () => "This is a DELETE");
    Other HTTP methods:

    ```C#
    app.MapMethods(new [] { "OPTIONS", "HEAD" }, () => "This is an options or head request ");
    app.MapMethods("/options-or-head", new [] { "OPTIONS", "HEAD" }, () => "This is an options or head request ");
    ```

    ### Route Parameters
  28. @davidfowl davidfowl revised this gist Sep 11, 2021. 1 changed file with 80 additions and 2 deletions.
    82 changes: 80 additions & 2 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -673,7 +673,37 @@ The rules for determining a binding source from a parameter are as follows:

    ## Responses

    The following example uses the built in result types to customize the response:
    Route handlers support 2 types of return values:

    1. `IResult` based - This includes `Task<IResult>` and `ValueTask<IResult>`
    1. `string` - This includes `Task<string>` and `ValueTask<string>`
    1. `T` (Any other type) - This includes `Task<T>` and `ValueTask<T>`

    |Return value|Behavior|Content-Type|
    |--|--|--|
    |`IResult` | The framework calls `IResult.ExecuteAsync`| Decided by the `IResult` implementation
    |`string` | The framework writes the string directly to the response | `text/plain`
    | `T` (Any other type) | The framework will JSON serialize the response| `application/json`

    **Example: string return values**

    ```C#
    app.MapGet("/hello", () => "Hello World");
    ```

    **Example: JSON return values**

    ```C#
    app.MapGet("/hello", () => new { Message = "Hello World" });
    ```

    **Example: IResult return values**

    ```C#
    app.MapGet("/hello", () => Results.Ok(new { Message = "Hello World" }));
    ```

    The following example uses the built-in result types to customize the response:

    ```C#
    app.MapGet("/todos/{id}", (int id, TodoDb db) =>
    @@ -725,7 +755,9 @@ app.MapGet("/old-path", () => Results.Redirect("/new-path"));
    app.MapGet("/download", () => Results.File("foo.text"));
    ```

    ### Built in results
    ### Built-in results

    Common result helpers exist on the `Microsoft.AspNetCore.Http.Results` static class.

    |Description|Response type|Status Code|API|
    |--|--|--|--|
    @@ -745,6 +777,52 @@ Write a JSON response with advanced options |application/json |200|`Results.Json

    ### Customizing results

    Users can take control of responses by implementing a custom `IResult` type. Here's an example of an HTML result type:

    ```C#
    namespace Microsoft.AspNetCore.Http;

    static class ResultsExtensions
    {
    public static IResult Html(this IResultExtensions resultExtensions, string html)
    {
    ArgumentNullException.ThrowIfNull(resultExtensions, nameof(resultExtensions));

    return new HtmlResult(html);
    }
    }

    class HtmlResult : IResult
    {
    private readonly string _html;

    public HtmlResult(string html)
    {
    _html = html;
    }

    public Task ExecuteAsync(HttpContext httpContext)
    {
    httpContext.Response.ContentType = MediaTypeNames.Text.Html;
    httpContext.Response.ContentLength = Encoding.UTF8.GetByteCount(_html);
    return httpContext.Response.WriteAsync(_html);
    }
    }
    ```

    We recommend adding an extension method to `Microsoft.AspNetCore.Http.IResultExtensions` to make these custom results more discoverable.

    ```C#
    app.MapGet("/html", () => Results.Extensions.Html(@$"<!doctype html>
    <html>
    <head><title>miniHTML</title></head>
    <body>
    <h1>Hello World</h1>
    <p>The time on the server is {DateTime.Now:O}</p>
    </body>
    </html>"));
    ```

    ## Authorization

    Routes can be protected using authorization policies. These can be declared via the authorize attribute or by using the `RequireAuthorization` method call.
  29. @davidfowl davidfowl revised this gist Sep 11, 2021. 1 changed file with 38 additions and 3 deletions.
    41 changes: 38 additions & 3 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -299,8 +299,6 @@ app.Run();

    Navigating to `/` will render a friendly page that shows the exception.

    TODO: Add image

    ### ASP.NET Core Middleware

    |Middleware |Description|API|
    @@ -410,8 +408,20 @@ app.MapGet("/posts/{slug:regex(^[a-z0-9_-]+$)}", (string slug) => $"Post {slug}"

    Parameter binding is the process of turning request data into strongly typed parameters that are expressed by route handlers. A binding source determines where parameters are bound from. Binding sources can be explict or inferred based HTTP method and parameter type.

    Supported binding sources:
    - Route values
    - Query string
    - Header
    - Body (as JSON)
    - Services provided by dependency injection
    - Custom

    ### GET, HEAD, OPTIONS, DELETE

    The HTTP methods GET, HEAD, OPTIONS, DELETE will never bind from body. All other binding sources are supported.

    **NOTE: If you need to support the case where you have a GET with a body, you can directly read it from the HttpRequest.**

    ```C#
    var builder = WebApplication.CreateBuilder(args);

    @@ -465,7 +475,7 @@ app.MapGet("/{id}", ([FromRoute]int id,

    |Parameter| Binding Source|
    |--|--|
    |id|route with the name id|
    |id|route value with the name id|
    |page|query string with the name `"p"`|
    |service|provided by dependency injection|
    |contentType|header with the name `"Content-Type"`|
    @@ -504,6 +514,18 @@ app.MapPost("/products", (Product? product) => () => { });

    The above will call the method will a null product if no request body was sent.

    **NOTE: If invalid data is provided and the parameter is nullable, the route handler will not be executed.**

    ```C#
    app.MapGet("/products", (int? pageNumber) => $"Requesting page {pageNumber ?? 1}");
    ```

    The following request will result in a 400 (see the **Binding Failures** section below for more details)

    ```
    GET /products?pageNumber=two
    ```

    ### Special types

    There are some special types that the framework supports binding without any explicit attributes:
    @@ -613,6 +635,19 @@ public enum SortDirection
    }
    ```

    ### Binding failures

    When binding fails, the framework will log a debug message and it will return various status codes to the client
    depending on the failure mode.

    |Failure mode|Nullable Parameter Type|Binding Source|Status code|
    |--|--|--|--|
    |`{ParameterType}.TryParse` returns false |yes|route/query/header|400|
    |`{ParameterType}.BindAsync` returns null |yes|custom|400|
    |`{ParameterType}.BindAsync` throws |does not matter|custom|500|
    | Failure to read JSON body |does not matter|body|400|
    | Wrong content type (not application/json) |does not matter|body|415|

    ### Binding Precedence

    The rules for determining a binding source from a parameter are as follows:
  30. @davidfowl davidfowl revised this gist Sep 11, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions MinimalAPIs.md
    Original file line number Diff line number Diff line change
    @@ -705,8 +705,8 @@ Write a JSON response with advanced options |application/json |200|`Results.Json
    |Set the status code to 422, with an optional JSON response | N/A |422|`Results.UnprocessableEntity`|
    |Set the status code to 400, with an optional JSON response | N/A |400|`Results.BadRequest`|
    |Set the status code to 409, with an optional JSON response | N/A |409|`Results.Conflict`|
    |Write a problem details JSON object to the response | N/A |400 (default), configurable|`Results.Problem`|
    |Write a problem details JSON object to the response with validation errors | N/A |400 (default), configurable|`Results.ValidationProblem`|
    |Write a problem details JSON object to the response | N/A |500 (default), configurable|`Results.Problem`|
    |Write a problem details JSON object to the response with validation errors | N/A | N/A, configurable|`Results.ValidationProblem`|

    ### Customizing results