3

I'm implementing a custom ASP.NET Core middleware to handle the ETag/If-Match pattern to prevent lost updates. My HTTP GET operations will return an ETag value in the header and every PUT operation will be required to include that ETag in the If-Match header. I'm hashing the body of the GET responses to generate the ETag value. I've currently implemented the middleware using the HttpClient to perform the GET operation when I need to check the If-Match header. This works but requires a network/out-of-process call. Shouldn't there be a better performing way to call the ASP.NET Core HTTP pipeline without leaving the process? Can the application generate a new request to itself without doing IO? Here's the code currently for my middleware:

public class ETagIfMatchMiddleware : IMiddleware
{
    //client for my asp.net core application
    public static HttpClient client = new HttpClient { BaseAddress = new Uri("https://localhost:5001") };

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        var request = context.Request;

        if (request.Method == HttpMethods.Put)
        {
            var ifMatch = request.Headers[HeaderNames.IfMatch];

            //requires network out of process call
            var response = await client.GetAsync(request.GetEncodedUrl());

            string eTag = response.Headers.ETag.Tag;

            if (eTag != ifMatch)
            {
                context.Response.StatusCode = StatusCodes.Status412PreconditionFailed;
                return;
            }
        }

        await next(context);
    }
}
Brett Janer
  • 517
  • 1
  • 4
  • 20
  • I don't think there will be a way to do that. For your scenario, I suggest that you could use the ResponseCache build in feature of asp.net core instead of writing your own., see docs here https://learn.microsoft.com/en-us/aspnet/core/performance/caching/middleware?view=aspnetcore-3.1 – Elendil Zheng-MSFT Aug 24 '20 at 03:01
  • Hi @ElendilZheng-MSFT, I don't see how the response caching feature solves my scenario. Would you mind elaborating? – Brett Janer Aug 24 '20 at 15:00
  • How is the GET call's ETag response header populated? Which middleware or code does this? – CodeCaster Sep 01 '20 at 20:53
  • @CodeCaster, I hash the body of the GET response. The implementation is very similar to this: https://gist.github.com/madskristensen/36357b1df9ddbfd123162cd4201124c4 – Brett Janer Sep 01 '20 at 21:18
  • You'll need to persist, cache, or be able to quickly recalculate the ETag for a given request, where all relevant request parameters indicate the storage key. Then you can look up the ETag for an incoming PUT request, by using that request's parameters. Of course now you have a new problem: cache invalidation. How will you keep your ETag cache and your source, for example database records that are used to generate the output, in sync? – CodeCaster Sep 01 '20 at 21:52
  • How to solve that depends on your actual intended usage. If the only relevant part of an ETag is a database record, and doesn't involve another transformation that happens in the controller or even a view, you could let a hash of your database's LastModified column be the ETag for that particular usage, instead of hashing the entire response body where all semantics of the response are lost. – CodeCaster Sep 01 '20 at 21:54
  • I did think about using the LastModified column on a record in the database, but some of my HTTP resources are made up of more than one database record. – Brett Janer Sep 01 '20 at 22:11

0 Answers0