Search code examples
c#asp.net-coremiddleware

Adding an Attribute for Response processing


Using C#, Net Core 3.1

I am wondering if someone can kindly help. I would like to add "middleware" but I think an Attribute is needed for my requirements as I want to do this selectively on specific API Actions only. How can I do some process (such as Signing a message and add it the response header) on a Response just before it is sent to the requesting client?

For example

    [HttpGet]
    [PostResponseProcessAttribute]
    public IActionResult GetFoo(int id) {
        return MyFoo();
    }

So for this GetFoo Action, I would like to do something like this:

public class PostResponseProcessAttribute : Attribute {
    public OnBeforeSendingResponse(){
      var response = Response.Content.ReadAsStringAsync;
     //do some stuff
      response.Headers.Add(results from stuff)
    }
}

Is it an Attribute that I need to implement and what is the function that I need override please? Also, a key bit is that Response is in the format and state that would be sent to to the client (i.e. passed other formatting middleware processing etc.) - this is important for Signing because any discrepancies would mean the client is verifying a response of a different version and therefore always fail.

Thanks


Solution

  • Finally, I wrote this:

    public sealed class AddHeadersRequiredAttribute : Attribute
    {
    }
    
    public class AddHeadersMiddleware
    {
        private readonly RequestDelegate _next;
    
        public AddHeadersMiddleware(RequestDelegate next)
        {
            _next = next;
        }
    
        public async Task Invoke(HttpContext context)
        {
            await using var memory = new MemoryStream();
            var originalStream = context.Response.Body;
            context.Response.Body = memory;
    
            await _next(context);
    
            memory.Seek(0, SeekOrigin.Begin);
            var content = await new StreamReader(memory).ReadToEndAsync();
            memory.Seek(0, SeekOrigin.Begin);
    
            // Now you can manipulate with content
            var attribute = GetAddHeadersRequiredAttributeFromMatchedAction(context);
            if (attribute != null)
                context.Response.Headers.Add("X-Header-1", content);
    
            await memory.CopyToAsync(originalStream);
            context.Response.Body = originalStream;
        }
    
        private Attribute GetAddHeadersRequiredAttributeFromMatchedAction(HttpContext context)
        {
            var endpoint = context.GetEndpoint();
            var controllerActionDescriptor = endpoint?.Metadata.GetMetadata<ControllerActionDescriptor>();
            return controllerActionDescriptor?.MethodInfo.GetCustomAttribute<AddHeadersRequiredAttribute>();
        }
    }
    

    You can mark controller method with AddHeadersRequiredAttribute. Then use middleware at your Startup.Configure method like this

    app.UseMiddleware<AddHeadersMiddleware>();