Optimize ASP.NET Core MVC Data Transfer with Custom Middleware

Introduction

In ASP.NET Core, middleware components are used to handle requests and responses as they flow through the application's pipeline. These middleware components can be chained together to process requests and responses in a specific order. Transferring data between middleware components can be achieved using various techniques. Here are a few commonly used methods.

HttpContext.Items

The HttpContext class in ASP.NET Core provides a dictionary-like collection (Items) that allows you to store and retrieve data within the scope of a single HTTP request. This data can be accessed by any middleware component in the request pipeline.

namespace _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares
{
    public class CustomMiddleware
    {
        private readonly RequestDelegate _next;

        public CustomMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            context.Items["SMAK"] = "SARDAR MUDASSAR ALI KHAN";
            // Add a custom header to the response
            context.Response.Headers.Append("X-Custom-Header", "Hello from CustomMiddleware");

            await _next(context);
        }
    }
}

Using constructor injection

You can pass data between middleware components using constructor injection. This approach is useful when you need to share data that is not specific to a single request.

namespace _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares
{
    public class MyMiddlewareOptions
    {
        public string OptionValue { get; set; }
    }
}
using Microsoft.Extensions.Options;

namespace _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares
{
    public class MyMiddlewareValues
    {
        private readonly RequestDelegate _next;
        private readonly MyMiddlewareOptions _options;

        public MyMiddlewareValues(RequestDelegate next, IOptions<MyMiddlewareOptions> options)
        {
            _next = next;
            _options = options.Value;
        }

        public async Task Invoke(HttpContext context)
        {
            // Use _options.OptionValue here
            await _next(context);
        }
    }
}

Using request and response objects

You can also pass data between middleware components by adding or modifying properties of the HttpRequest and HttpResponse objects.

namespace _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares
{
    public class CustomMiddleware
    {
        private readonly RequestDelegate _next;

        public CustomMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            // Add a custom header to the response
            context.Response.Headers.Append("X-Custom-Header", "Hello from CustomMiddleware");

            await _next(context);
        }
    }
}

Custom middleware options

If you have a significant amount of data to transfer between middleware components, you can create custom middleware options and configure them when registering your middleware in the application startup.

namespace _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares
{
    public class MyMiddlewareOptions
    {
        public string OptionValue { get; set; }
    }
}
using Microsoft.Extensions.Options;

namespace _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares
{
    public class MyMiddlewareValues
    {
        private readonly RequestDelegate _next;
        private readonly MyMiddlewareOptions _options;

        public MyMiddlewareValues(RequestDelegate next, IOptions<MyMiddlewareOptions> options)
        {
            _next = next;
            _options = options.Value;
        }

        public async Task Invoke(HttpContext context)
        {
            // Use _options.OptionValue here
            await _next(context);
        }
    }
}

Now we will create the New Project in the asp.net core Mvc Project and will use this technique to transfer the data between controllers.

Create a new ASP.NET Core MVC project

You can create a new ASP.NET Core MVC project using Visual Studio or the .NET CLI.

dotnet new mvc -n _100_DataTransferUsingMiddlewareInMVCCore

Define a custom middleware component

Create a new class for your custom middleware. This middleware will add a custom header to the HTTP response.

namespace _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares
{
    public class CustomMiddleware
    {
        private readonly RequestDelegate _next;

        public CustomMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            // Add a custom header to the response
            context.Response.Headers.Append("X-Custom-Header", "Hello from CustomMiddleware");

            await _next(context);
        }
    }
}

Pass the Data Using HTTP Context Items

namespace _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares
{
    public class CustomMiddleware
    {
        private readonly RequestDelegate _next;

        public CustomMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            context.Items["SMAK"] = "SARDAR MUDASSAR ALI KHAN";
            // Add a custom header to the response
            await _next(context);
        }
    }
}

Pass the Values Using HTTP Custom Middleware

Create the Value Options Class.

namespace _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares
{
    public class MyMiddlewareOptions
    {
        public string OptionValue { get; set; }
    }
}

Now Create the Custom Middleware.

using Microsoft.Extensions.Options;

namespace _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares
{
    public class MyMiddlewareValues
    {
        private readonly RequestDelegate _next;
        private readonly MyMiddlewareOptions _options;

        public MyMiddlewareValues(RequestDelegate next, IOptions<MyMiddlewareOptions> options)
        {
            _next = next;
            _options = options.Value;
        }

        public async Task Invoke(HttpContext context)
        {
            // Use _options.OptionValue here
            await _next(context);
        }
    }
}

Configure All Middleware in the Program.cs class.

using _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.Configure<MyMiddlewareOptions>(options =>
{
    options.OptionValue = "Sardar Mudassar Ali Khan Value From Options Middlleware";
});
var app = builder.Build();

// Configure the HTTP request pipeline.
if(!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

// Add the custom middleware to the pipeline
app.UseMiddleware<CustomMiddleware>();
app.UseMiddleware<MyMiddlewareValues>();

app.UseEndpoints(configure: endpoints =>
{
    endpoints.MapControllerRoute(
        name: "Middleware",
        pattern: "{controller=Middleware}/{action=Index}/{id?}");
});

app.MapRazorPages();

app.Run();

Now Create the Controller and use the Middlewares.

using _100_DataTransferUsingMiddlewareInMVCCore.CustomMiddlewares;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;

namespace _100_DataTransferUsingMiddlewareInMVCCore.Controllers
{
    public class MiddlewareController : Controller
    {
        private readonly MyMiddlewareOptions _middlewareOptions;

        public MiddlewareController(IOptions<MyMiddlewareOptions> middlewareOptions)
        {
            _middlewareOptions = middlewareOptions.Value;
        }
        public IActionResult Index()
        {
            // Access the custom header added by the middleware
            var customHeaderValue = HttpContext.Response.Headers["X-Custom-Header"];

            // Access the data stored in HttpContext.Items["SMAK"]
            var data = HttpContext.Items["SMAK"] as string;

            var dataValue = _middlewareOptions.OptionValue ?? string.Empty;

            // Set ViewBag with both custom header value and data
            ViewBag.CustomHeader = customHeaderValue;
            ViewBag.Data = data;
            ViewBag.dataValue = dataValue;

            return View();
        }

    }
}

Create the View and show the data.

<!-- Index.cshtml -->
@{
    Layout = "_Layout";
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Custom Header Value: @ViewBag.CustomHeader</p>
    <p>Custom Header Value Using HttpContext Items: @ViewBag.Data</p>
    <p>Custom Header Value Using Custom Middleware: @ViewBag.dataValue</p>
</div>

Output

Output

Github Project Link: https://github.com/SardarMudassarAliKhan/100-DataTransferUsingMiddlewareInMVCCore

Conclusion

Implementing custom middleware in an ASP.NET Core MVC application provides a powerful mechanism for intercepting and processing HTTP requests and responses. Through middleware, developers can customize the request pipeline to perform various tasks such as logging, authentication, data manipulation, and more.

By leveraging the HttpContext and the options pattern, data can be efficiently transferred between middleware components and accessed within controllers and views. The dependency injection container simplifies the management of middleware dependencies, enhancing the modularity and maintainability of the application.

Custom middleware in ASP.NET Core MVC offers flexibility, extensibility, and control over the request-handling process, enabling developers to build robust and tailored web applications to meet specific business requirements.


Similar Articles