0

The following code is part of an error handling middleware, whose goal is to provide consistent formatting for the client even if an error is thrown.

I am trying to serialize a response as XML when the Accept header is set to application/xml, otherwise return JSON. This article helped get me started: https://www.devtrends.co.uk/blog/handling-errors-in-asp.net-core-web-api

if (context.Request.Headers["Accept"] == "application/xml")
{
    context.Response.ContentType = "application/xml";

    using (var stringwriter = new StringWriter())
    {
        var serializer = new XmlSerializer(response.GetType());
        serializer.Serialize(stringwriter, response);
        await context.Response.WriteAsync(stringwriter.ToString());
    }
}
else {
    context.Response.ContentType = "application/json";
    var json = JsonConvert.SerializeObject(response);
    await context.Response.WriteAsync(json);
}

The else block works as expected. If I set a breakpoint on the line where the XmlSerializer is declared, execution halts. If I set a breakpoint on the following line, the breakpoint is never hit; a response has already been sent to the client.

My middleware is configured like this:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseStatusCodePagesWithReExecute("/error/{0}");
        app.UseExceptionHandler("/error/500");
        app.UseHsts();
        app.UseMiddleware<ErrorWrappingMiddleware>();
        app.UseMvc();
    }

Why is a response returned to the client before context.Response.WriteAsync(stringwriter.ToString()); is called in the if block?

McGlothlin
  • 2,059
  • 1
  • 15
  • 28
  • 1
    WebAPi has content-negotiation built in (https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-2.2), Is there any reason to write this middleware? – Vivek Natarajan Jul 19 '19 at 23:51
  • I've seen this page and added `AddXmlSerializerFormatters()` to my configuration, but when an error is returned (status code 500 for instance) the response body is blank. I want a consistently formatted response body, hence the additional middleware. – McGlothlin Jul 22 '19 at 14:40
  • When using XML formatters, return only types don't return IEnumerable of types this will throw exception. – Vivek Natarajan Jul 23 '19 at 16:19
  • Can we get a more complete example? Also what happens if you set a try catch around var serializer = new XmlSerializer(response.GetType()); serializer.Serialize(stringwriter, response); await context.Response.WriteAsync(stringwriter.ToString()); – Jonathan Alfaro Jul 26 '19 at 19:47

1 Answers1

1

I figured out the problem, it was a silly mistake.

To test 500 errors, I was intentionally adding a property to a model that didn't have a corresponding column in the database. This throws a SqlException on an invalid column name. There was however a second exception I missed that told me exactly what the problem was:

ApiResponse cannot be serialized because it does not have a parameterless constructor.

This occurred in the call to serializer.Serialize(stringwriter, response). The example I referenced creates an ApiResponse class (the response I attempted to serialize). However this class does not have a parameterless constructor in the example I was learning from, and I wasn't aware this was necessary until I found this answer. This second exception halted execution, but since I forgot to turn on development mode and I was trying to throw an exception, I didn't notice the second one.

McGlothlin
  • 2,059
  • 1
  • 15
  • 28