• Rik Boeykens

  • Software Engineer

More control over requests and responses.

If you want more control of your .NET Core 2.0 application, building custom middleware for it is the way to go. With custom middleware you’re able to exert much greater control over the request and responses that flow through the app pipeline. In this post I’ll show how to set it up and how to do some basic logging of incoming requests.

The Basic Setup

The basic setup of the CustomMiddleware class looks like this:

public class CustomMiddleware
{
private readonly RequestDelegate _next;
 
public CustomMiddleware(
RequestDelegate next
)
{
_next = next;
_logger = logger;
}

public async Task Invoke(HttpContext context)
{
await _next(context);
}
}

We get the next RequestDelegate through dependency injection and set up an asynchronous Invoke method with the HttpContext as input. We input the context into the RequestDelegate to perform it.

Configuring the application

We now need to configure our application to use the custom middleware. We do this in our startup.cs in the Configure method:

app.UseMiddleware<CustomMiddleware>();

Every request to the application will now pass through the Invoke method. Next, we want to log incoming requests.

In our Invoke method we have access to the HttpContext. We can use this to extract the url used to call the application:

private string GetDisplayUrl(HttpContext context)
{
string str1 = context.Request.Host.Value;
string str2 = context.Request.PathBase.Value;
string str3 = context.Request.Path.Value;
string str4 = context.Request.QueryString.Value;
return new StringBuilder(context.Request.Scheme.Length + "://".Length + str1.Length + str2.Length + str3.Length + str4.Length).Append(context.Request.Scheme).Append("://").Append(str1).Append(str2).Append(str3).Append(str4).ToString();
}

As you can see, we have access to the request and its host, pathbase, path, and querystring.

We can also see the headers. We use this to get the Authorization token:

private string GetToken(HttpContext context)
{
return context.Request.Headers.FirstOrDefault(x =>
string.Equals(x.Key, "Authorization", StringComparison.OrdinalIgnoreCase)).Value.FirstOrDefault();
}

 We use the url and token to log all incoming requests in the Invoke method:

public async Task Invoke(HttpContext context)
{
var token = GetToken(context);
var url = GetDisplayUrl(context);
_logger.LogInformation($"{token ?? "Unknown user"} is accessing {url}");
await _next(context);
}

The result is that whenever we do a request (for instance with a bearer token of 1234 and an extra parameter ‘foo’) this is logged:

Bearer 1234 is accessing http://localhost:5000/api/values?extraParam=foo

In Conclusion

We now have a centralised spot where all incoming requests come through. We can and will expand on this by setting up an exception mapper that automatically handles all exceptions thrown in the application, in a next blogpost.

The code used in this example can be found here on GitHub.