Home > front end >  Custom middleware breaks Developer Exception Page
Custom middleware breaks Developer Exception Page

Time:05-13

Custom middleware breaks Developer Exeption Page. Instead of page with exception details there is HTTP 500 response. Debugging line by line runs only until await _next(context);.

Once the custom middleware is removed from WebApplication configuration by deleting app.UseResponseTime(); all works fine.

I suspect the problem is connected to Response stream I am interfering in, but have no more ideas how to diagnose the problem further and solve it.

Using ASP.NET Core 6.

public class ResponseTimeMiddleware
{
    private readonly RequestDelegate _next;

    public ResponseTimeMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var originalBody = context.Response.Body;
        var newBody = new MemoryStream();
        context.Response.Body = newBody;

        var sw = new Stopwatch();
        sw.Start();

        await _next(context);

        sw.Stop();

        // read the new body
        newBody.Position = 0;
        var newContent = await new StreamReader(newBody).ReadToEndAsync();

        // calculate the updated html
        var updatedHtml = UpdateHtmlElement(newContent, sw.ElapsedMilliseconds);

        // set the body = updated html
        var updatedStream = GenerateStreamFromString(updatedHtml);
        await updatedStream.CopyToAsync(originalBody);
        context.Response.Body = originalBody;
    }

    private static Stream GenerateStreamFromString(string s)
    {
        // convert string to stream
        return new MemoryStream(System.Text.Encoding.UTF8.GetBytes(s ?? ""));
    }

    private static string UpdateHtmlElement(string originalHtml, long responseTime)
    {
        var htmlDoc = new HtmlDocument();
        htmlDoc.LoadHtml(originalHtml);

        var ProcTimeHtmlElement = htmlDoc.GetElementbyId("proctime");
        if (ProcTimeHtmlElement != null)
        {
            ProcTimeHtmlElement.InnerHtml = $"{responseTime} ms";
        }

        return htmlDoc.DocumentNode.OuterHtml;
    }
}

public static class ResponseTimeMiddlewareExtensions
{
    public static IApplicationBuilder UseResponseTime(
        this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<ResponseTimeMiddleware>();
    }
}

Very standard Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllers();
builder.Services.AddHttpContextAccessor();

var app = builder.Build();

app.UseResponseTime();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();
app.MapControllers();

app.Run();

CodePudding user response:

I had this kind of problem in the past, and I solved it by changing the line of the custom middleware, I don't know it works for you but it doesn't hurt to try, I hope it helps.

CodePudding user response:

It was caused beacuse you replaced your default reponse body with MemoryStream I tried as below and could see the err page,but I don't think it's a good solution:

public async Task InvokeAsync(HttpContext context)
        
        {

            var originalBody = context.Response.Body;
            var newBody = new MemoryStream();
            var sw = new Stopwatch();
            sw.Start();
            try
            {
                
                await _next(context);
            }
            catch (Exception ex) 
            {
                context.Response.Body = originalBody;
                await _next(context);
            }
            
            newBody.Position = 0;
            sw.Stop();            
            // read the new body                        
            var newContent = await new StreamReader(newBody).ReadToEndAsync();
            // calculate the updated html
            var updatedHtml = UpdateHtmlElement(newContent, sw.ElapsedMilliseconds);
            // set the body = updated html
            var updatedStream = GenerateStreamFromString(updatedHtml);
            await updatedStream.CopyToAsync(originalBody); 
            context.Response.Body = originalBody;
        }

enter image description here

Update

I tried to custom a error handle middleware and it worked:

 public class ExceptionHandlerMiddleware
    {
        private readonly RequestDelegate _next;
        
        public ExceptionHandlerMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context)
        {
            var originalBody = context.Response.Body;
            try
            {
                await _next.Invoke(context);
            }
            catch (Exception ex)
            {
                 context.Response.Body=originalBody;
                await HandleExceptionMessageAsync(context, ex).ConfigureAwait(false);
            }
        }
        private static Task HandleExceptionMessageAsync(HttpContext context, Exception exception)
        {
            context.Response.ContentType = "application/json";
            int statusCode = (int)HttpStatusCode.InternalServerError;
            var result = JsonConvert.SerializeObject(new
            {
                StatusCode = statusCode,
                ErrorMessage = exception.Message
            });
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = statusCode;
            return context.Response.WriteAsync(result);
        }
    }

In program.cs and before other middleware:

app.UseMiddleware<xxx.ExceptionHandlerMiddleware>();

The result: enter image description here

  • Related