'Headers are read-only, response has already started

I am trying to catch and format the exception thrown by the resource filter but getting this error. The middleware is working for exceptions thrown from controller level but getting this - "System.InvalidOperationException: Headers are read-only, response has already started" error while trying to write to the response in case of resource level errors.

Code of my Resource Filter:

public class TestingAsyncResourceFilter : IAsyncResourceFilter
{
    public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
    {
        Console.WriteLine("Resource filter executing");
        var resourceExecutedContext = await next();
        Console.WriteLine("Resource filter executed");
        if (!resourceExecutedContext.ModelState.IsValid)
        {
            throw new CustomUPException();
        }
    }
}

Code of middleware:

public class ResponseFormatterMiddleware : IMiddleware
{
    private readonly ILogger<ResponseFormatterMiddleware> _logger;

    public ResponseFormatterMiddleware(ILogger<ResponseFormatterMiddleware> logger)
    {
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        try
        {
            Console.WriteLine("Before execution");
            await next(context);
            Console.WriteLine("After Execution");
        }
        catch(CustomUPException e)
        {
            Console.WriteLine("Here we are");
            await context.Response.WriteAsJsonAsync(
                new ResponseDto()
                {
                    statusCode = e.statusCode,
                    message = e.message
                }); // getting error
        }
        catch(Exception e)
        {
            _logger.LogError(e.Message);
            context.Response.StatusCode = (int) HttpStatusCode.InternalServerError;
            await context.Response.WriteAsJsonAsync(
                 new ResponseDto()
                 {
                     success = false,
                     message = "Request failed"
                 });
        }
    }
}

Code of my controller:

[Route("api/[controller]")]
[ApiController]
public class TestingController : ControllerBase
{
    [HttpPost("/resource")]
    public async Task<UserDto> testingResource( [FromBody] UserDto dto)
    {
        if (dto.email.Contains("hell"))
        {
            throw new CustomUPException(); //working
        }
        return dto;
    }
}


Solution 1:[1]

Instead of using resource filter, I have used this strategy for formatting model validation errors as the documentation suggests.

Curious to know more about the raised issue though. Thanks in advance

    // Add services to the container.
builder.Services.AddControllers().ConfigureApiBehaviorOptions(
    options =>
    {
        options.InvalidModelStateResponseFactory = context =>
        {
            if (!context.ModelState.IsValid)
            {
                var data = new Dictionary<string, string?>();
                //My Response formatter
                var modelStateDictionary = context.ModelState;
                foreach (var key in modelStateDictionary.Keys)
                {
                    var errors = modelStateDictionary[key]?.Errors;
                    data.TryAdd(key, errors?[0].ErrorMessage);
                }
                return new ObjectResult(new UniversalResponseDto()
                {
                    data = data,
                    statusCode = (int)HttpStatusCode.UnprocessableEntity,
                    sucess = false,
                    message = "One or more validation error occured"
                })
                {
                    StatusCode = (int)HttpStatusCode.UnprocessableEntity,
                };
            }
            return new ObjectResult(context.HttpContext.Response);
        };
    });

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1