So we have a middleware that takes the response body of our API responses and wraps it in an ApiResult class under a "data" property.
namespace Web.Api.ApiResult
{
public class ApiResultMiddleware
{
private readonly RequestDelegate next;
public ApiResultMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
var originalBody = context.Response.Body;
var responseBody = string.Empty;
try
{
CsWebException exception = null;
using (var stream = new MemoryStream())
{
context.Response.Body = stream;
try
{
await next.Invoke(context);
}
catch (CsWebException e)
{
exception = e;
}
stream.Position = 0;
responseBody = new StreamReader(stream).ReadToEnd();
}
object result = null;
if (exception != null)
{
result = new ApiResultResponse(null)
{
ErrorCode = exception.ErrorCode,
ErrorData = exception.ErrorData,
};
context.Response.StatusCode = (int)ApiResultHttpStatusCodeConverter.ConvertToHttpStatusCode(exception.ErrorCode);
}
else
{
var data = JsonConvert.DeserializeObject(responseBody);
result = new ApiResultResponse(data);
}
var buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(
result,
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
if (context.Response.StatusCode != StatusCodes.Status204NoContent)
{
using (var output = new MemoryStream(buffer))
{
var test = JsonConvert.DeserializeObject<ApiResultResponse>(new StreamReader(output).ReadToEnd());
await output.CopyToAsync(originalBody);
}
}
}
catch (JsonReaderException)
{
var result = new ApiResultResponse(responseBody);
var buffer = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(
result,
new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }));
using (var output = new MemoryStream(buffer))
{
await output.CopyToAsync(originalBody);
}
}
finally
{
context.Response.Body = originalBody;
}
}
}
public static class ApiResultMiddlewareExtensions
{
public static IApplicationBuilder UseApiResultMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<ApiResultMiddleware>();
}
}
}
It worked perfectly in .NET core 2.2 and even after migrating to 3.1 and using the build in Sytem.Text.Json but because we need newtonsoft in our patch endpoints. By adding it with
services.AddControllers().AddNewtonsoftJson();
the returned JSON gets cut somewhere in the middle. I added the test variable and deserialized the JSON just before writing it and it looks fine but when parsing i in our front end it's not.
An example is where the json beeing written to the body looks like:
"{\"errorCode\":0,\"errorData\":null,\"data\":{\"previousWorkingDayWorkOrders\":[],\"nextWorkingDayWorkOrders\":[],\"todaysWorkOrders\":[],\"nonInvoicedWorkOrders\":[{\"workOrderId\":1232753.0,\"employeeNumber\":5000000037.0,\"employeeName\":\"VERKADM, VERKADM\",\"vehicleRegistrationNumber\":\"PXG948\",\"dealerOrderNumber\":null,\"bookingNumber\":null,\"preplannedDate\":null,\"workOrderStatus\":7,\"shortageIndicator\":true,\"customerWaiting\":false,\"vehicleDescriptionShort\":\"Volvo V40 Cross Country\",\"vehicleModelYear\":2018,\"colorDescription\":\"Blå\",\"fuelDescription\":\"Diesel\",\"email\":null,\"customer\":{\"customerNumber\":null,\"name\":\"Volvo Bil I Göteborg AB\",\"telephone\":null,\"email\":null,\"customerType\":1},\"mainPayerCustomerType\":1,\"notes\":null,\"vehicleIdentificationNumber\":\"YV1MZ79L0J2139968\"}],\"webWorkOrders\":[]}}"
When receiving the same response in postman it can't be parsed to json because it is cut off and in plan text it looks like:
{"errorCode":0,"errorData":null,"data":{"previousWorkingDayWorkOrders":[],"nextWorkingDayWorkOrders":[],"todaysWorkOrders":[{"workOrderId":1229253.0,"employeeNumber":5000000037.0,"employeeName":"VERKADM, VERKADM","vehicleRegistrationNumber":"PXG948","dealerOrderNumber":null,"bookingNumber":"349","preplannedDate":"2020-02-06T07:00:00","workOrderStatus":5,"shortageIndicator":true,"customerWaiting":false,"vehicleDescriptionShort":"Volvo V40 Cross Country","vehicleModelYear":2018,"colorDescription":"Blå","fuelDescription":"Diesel","email":null,"customer":{"customerNumber":null,"name":"Volvo Bil I Göteborg AB","telephone":null,"email":null,"customerType":1},"mainPayerCustomerType":1,"notes":null,"vehicleIdentificationNumber":"YV1MZ79L0J2139968"}],"nonInvoicedWorkOrders":[{"workOrderId":1232753.0,"employeeNumber":5000000037.0,"employeeName":"VERKADM, VERKADM","vehicleRegistrationNumber":"PXG948","dealerOrderNumber":null,"bookingNumber":null,"preplannedDate":null,"workOrderStatus":7,"shortageIndicator":true,"customerWaiting":false,"vehicleDescriptionShort":"Volvo V40 Cross Country","vehicleModelYear":2018,"colorDescription":"Blå","fuelDescription":"Diesel","email":null,"customer":{"customerNumber":null,"name":"Volvo Bil I Göteborg AB","telephone":null,"email":null,"customerType":1},"mainPayerCustomerType":1,"notes":null,"vehicleIdentificationNumber":"Y
Any idea of why this is not working?
Edit: I should also point out that by removing the middleware app.UseApiResultMiddleware();
everything workes fine but we still want to wrap our responses
Edit 2. I managed to solve this thanks to dbc's response. By setting the length of response content to the length of the buffer it works perfectly.
context.Response.ContentLength = buffer.Length;
What puzzles me is that fact that is was working without setting the length with System.Text.Json and when we used .Net core 2.2