1

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

1
  • You can't pass any data to attribute at runtime. So you should write middleware which will do all work and set required headers. You can use attribute in your case just for mark methods which need be processed. Commented Oct 1, 2020 at 15:56

1 Answer 1

1

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>();
Sign up to request clarification or add additional context in comments.

2 Comments

You can pass it at UseMiddleware like that: app.UseMiddleware<AddHeadersMiddleware>(app.ApplicationServices.GetService<IService>()); and add IService to middleware constructor arguments. You should register IService as singleton if it will be used just in middleware, because middleware will be created once.
Got your example to work! Thank you. Hand to ensure to add it early in the Startup Configure()!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.