# Middleware

Middleware is software that's assembled into an app pipeline to handle requests and responses. Each component performs operations on an `HttpContext` and:

* Chooses whether to pass the request to the next component in the pipeline.
* Can perform work before and after the next component in the pipeline.

Request delegates are used to build the request pipeline. The request delegates handle each HTTP request. Request delegates are configured using `Run`, `Map`, and `Use` extension methods.&#x20;

> An individual request delegate can be specified in-line as an anonymous method (called in-line middleware), or it can be defined in a reusable class. These reusable classes and in-line anonymous methods are *middleware*, also called *middleware components*. Each middleware component in the request pipeline is responsible for invoking the next component in the pipeline or short-circuiting the pipeline. When a middleware short-circuits, it's called a *terminal middleware* because it prevents further middleware from processing the request.

The request handling pipeline is composed as a series of middleware components. Each component performs operations on an `HttpContext` and either invokes the next middleware in the pipeline or terminates the request.

{% code title="Program.cs" %}

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

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthorization();

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

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.Run();
```

{% endcode %}

{% embed url="<https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/index/_static/request-delegate-pipeline.png?view=aspnetcore-6.0>" %}

* Chain multiple request delegates together with `Use`. The `next` parameter represents the next delegate in the pipeline. You can short-circuit the pipeline by *not* calling the `next` parameter.

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

app.Use(async (context, next) => {
    // Do work that can write to the Response.
    await next.Invoke();
    // Do logging or other work that doesn't write to the Response.
});

app.Run();
```

* When a delegate doesn't pass a request to the next delegate, it's called *short-circuiting the request pipeline*. Short-circuiting is often desirable because it avoids unnecessary work. \
  For example, *Static File Middleware* can act as a *terminal middleware* by processing a request for a static file and short-circuiting the rest of the pipeline.

{% embed url="<https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/index/_static/middleware-pipeline.svg?view=aspnetcore-6.0>" %}
Middleware order for ASP.NET Core MVC and Razor Pages apps
{% endembed %}

If you don't call `app.UseRouting`, the **Routing** middleware runs at the beginning of the pipeline by default.

The **Endpoint** middleware in the preceding diagram executes the filter pipeline for the corresponding app type—MVC or Razor Pages.

{% embed url="<https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/index/_static/mvc-endpoint.svg?view=aspnetcore-6.0>" %}

* `Map` extensions are used as a convention for branching the pipeline. `Map` branches the request pipeline based on matches of the given request path. If the request path starts with the given path, the branch is executed.
  * `Map` supports nesting.
  * `Map` can also be used to match multiple segments at once.
  * `MapWhen` branches the request pipeline based on the result of the given predicate.

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

app.Map("/map1", HandleMapTest1);
app.Map("/level1", level1App => {
    level1App.Map("/level2a", level2AApp => {
        // "/level1/level2a" processing
    });
    level1App.Map("/level2b", level2BApp => {
        // "/level1/level2b" processing
    });
});
app.Map("/map1/seg1", HandleMultiSeg);
app.MapWhen(context => context.Request.Query.ContainsKey("branch"), HandleBranch);

app.Run(async context => {
    await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
});

app.Run();

static void HandleMapTest1(IApplicationBuilder app) {
    app.Run(async context => { 
        await context.Response.WriteAsync("Map Test"); 
    });
}

static void HandleMultiSeg(IApplicationBuilder app) {
    app.Run(async context => { 
        await context.Response.WriteAsync("Multiple Segment Test"); 
    });
}

static void HandleBranch(IApplicationBuilder app) {
    app.Run(async context => {
        var branchVer = context.Request.Query["branch"];
        await context.Response.WriteAsync($"Branch used = {branchVer}");
    });
}
```

The following table shows the requests and responses from `http://localhost:1234` using the preceding code.

| Request                     | Response                     |
| --------------------------- | ---------------------------- |
| localhost:1234              | Hello from non-Map delegate. |
| localhost:1234/map1         | Map Test                     |
| localhost:1234/map1/seg1    | Multiple Segment Test        |
| localhost:1234/?branch=main | Branch used = main           |
| localhost:1234/map3         | Hello from non-Map delegate. |

When `Map` is used, the matched path segments are removed from `HttpRequest.Path` and appended to `HttpRequest.PathBase` for each request.

* `Run` delegates don't receive a `next` parameter. The first `Run` delegate is always terminal and terminates the pipeline. Some middleware components may expose `Run[Middleware]` methods that run at the end of the pipeline:

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

app.UseHttpsRedirection();
app.UseStaticFiles();

app.Run(async context => {
    await context.Response.WriteAsync("Hello from 2nd delegate.");
});

app.Run();
```

* If another `Use` or `Run` delegate is added after the `Run` delegate, it's not called.

## Test Middleware <a href="#test-aspnet-core-middleware" id="test-aspnet-core-middleware"></a>

Middleware can be tested in isolation with TestServer. It allows you to:

* Instantiate an app pipeline containing only the components that you need to test.
* Send custom requests to verify middleware behavior.

```csharp
[Fact]
public async Task MiddlewareTest_ReturnsNotFoundForRequest() {
    // Arrange
    using var host = await new HostBuilder()
        .ConfigureWebHost(webBuilder => {
            webBuilder
                .UseTestServer()
                .ConfigureServices(services => {
                    services.AddMyServices();
                })
                .Configure(app => {
                    app.UseMiddleware<MyMiddleware>();
                });
        })
        .StartAsync();
    
    // Action
    // send a request using HttpClient
    var response = await host.GetTestClient().GetAsync("/");

    // send a request using HttpContext    
    var server = host.GetTestServer();
    server.BaseAddress = new Uri("https://example.com/A/Path/");

    var context = await server.SendAsync(c =>
    {
        c.Request.Method = HttpMethods.Post;
        c.Request.Path = "/and/file.txt";
        c.Request.QueryString = new QueryString("?and=query");
    });
    
    // Assert
    Assert.NotEqual(HttpStatusCode.NotFound, response.StatusCode);
    Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
    
    Assert.True(context.RequestAborted.CanBeCanceled);
    Assert.Equal(HttpProtocol.Http11, context.Request.Protocol);
    Assert.Equal("POST", context.Request.Method);
    Assert.Equal("https", context.Request.Scheme);
    Assert.Equal("example.com", context.Request.Host.Value);
    Assert.Equal("/A/Path", context.Request.PathBase.Value);
    Assert.Equal("/and/file.txt", context.Request.Path.Value);
    Assert.Equal("?and=query", context.Request.QueryString.Value);
    Assert.NotNull(context.Request.Body);
    Assert.NotNull(context.Request.Headers);
    Assert.NotNull(context.Response.Headers);
    Assert.NotNull(context.Response.Body);
    Assert.Equal(404, context.Response.StatusCode);
    Assert.Null(context.Features.Get<IHttpResponseFeature>().ReasonPhrase);
    
}
```

## Custom Middleware

Creating a middleware component by calling `Microsoft.AspNetCore.Builder.UseExtensions.Use`. The `Use` extension method adds a middleware delegate defined in-line to the application's request pipeline.

There are two overloads available for the `Use` extension:

* One takes a `HttpContext` and a `Func<Task>`. \
  Invoke the `Func<Task>` without any parameters.
* The other takes a `HttpContext` and a RequestDelegate. \
  Invoke the `RequestDelegate` by passing the `HttpContext`.

{% tabs %}
{% tab title="RequestCultureMiddleware.cs" %}

```csharp
using System.Globalization;

namespace Middleware.Example;

public class RequestCultureMiddleware {
    private readonly RequestDelegate _next;

    // A public constructor with a parameter of type RequestDelegate.
    public RequestCultureMiddleware(RequestDelegate next) {
        _next = next;
    }

    /*
        A public method named Invoke/InvokeAsync. This method must:
            - Return a Task.
            - Accept a first parameter of type HttpContext.
    */
    public async Task InvokeAsync(HttpContext context) {
        var cultureQuery = context.Request.Query["culture"];
        if (!string.IsNullOrWhiteSpace(cultureQuery)) {
            var culture = new CultureInfo(cultureQuery);
            CultureInfo.CurrentCulture = culture;
            CultureInfo.CurrentUICulture = culture;
        }

        // Call the next delegate/middleware in the pipeline.
        await _next(context);
    }
}

// exposing the middleware through IApplicationBuilder
public static class RequestCultureMiddlewareExtensions {
    public static IApplicationBuilder UseRequestCulture(
        this IApplicationBuilder builder) {
        return builder.UseMiddleware<RequestCultureMiddleware>();
    }
}
```

{% endtab %}

{% tab title="Program.cs" %}

```csharp
using Middleware.Example;
using System.Globalization;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.UseHttpsRedirection();

// Custom Middleware
app.UseRequestCulture();

app.Run(async (context) => {
    await context.Response.WriteAsync(
        $"CurrentCulture.DisplayName: {CultureInfo.CurrentCulture.DisplayName}");
});

app.Run();
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://dailyjournal.gitbook.io/notes/web-frameworks/asp.net-core/fundamentals/middleware.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
