Skip to content

Commit

Permalink
Update test option for .net50
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexPaskhin committed Oct 8, 2020
1 parent 4e65253 commit eece75f
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public void Configure(MvcOptions options)
options.ModelBinderProviders.Add(new HeaderModelBinderProvider());
options.ModelBinderProviders.Add(new FloatingPointTypeModelBinderProvider());
options.ModelBinderProviders.Add(new EnumTypeModelBinderProvider(options));
options.ModelBinderProviders.Add(new DateTimeModelBinderProvider());
options.ModelBinderProviders.Add(new SimpleTypeModelBinderProvider());
options.ModelBinderProviders.Add(new CancellationTokenModelBinderProvider());
options.ModelBinderProviders.Add(new ByteArrayModelBinderProvider());
Expand All @@ -88,7 +89,7 @@ public void Configure(MvcOptions options)
options.OutputFormatters.Add(new StringOutputFormatter());
options.OutputFormatters.Add(new StreamOutputFormatter());

var jsonOutputFormatter = LocSystemTextJsonOutputFormatter.CreateFormatter(_jsonOptions.Value);
var jsonOutputFormatter = TestSystemTextJsonOutputFormatter.CreateFormatter(_jsonOptions.Value);
options.OutputFormatters.Add(jsonOutputFormatter);

// Set up ValueProviders
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,24 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.IO;
using System.Runtime.ExceptionServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Formatters.Json;

namespace Microsoft.AspNetCore.Mvc.Formatters
{
/// <summary>
/// A <see cref="TextOutputFormatter"/> for JSON content that uses <see cref="JsonSerializer"/>.
/// </summary>
public class LocSystemTextJsonOutputFormatter : TextOutputFormatter
public class TestSystemTextJsonOutputFormatter : TextOutputFormatter
{
/// <summary>
/// Initializes a new <see cref="LocSystemTextJsonOutputFormatter"/> instance.
/// Initializes a new <see cref="TestSystemTextJsonOutputFormatter"/> instance.
/// </summary>
/// <param name="jsonSerializerOptions">The <see cref="JsonSerializerOptions"/>.</param>
public LocSystemTextJsonOutputFormatter(JsonSerializerOptions jsonSerializerOptions)
public TestSystemTextJsonOutputFormatter(JsonSerializerOptions jsonSerializerOptions)
{
SerializerOptions = jsonSerializerOptions;

Expand All @@ -33,24 +30,27 @@ public LocSystemTextJsonOutputFormatter(JsonSerializerOptions jsonSerializerOpti
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationAnyJsonSyntax);
}

internal static LocSystemTextJsonOutputFormatter CreateFormatter(JsonOptions jsonOptions)
internal static TestSystemTextJsonOutputFormatter CreateFormatter(JsonOptions jsonOptions)
{
var jsonSerializerOptions = jsonOptions.JsonSerializerOptions;

if (jsonSerializerOptions.Encoder is null)
{
// If the user hasn't explicitly configured the encoder, use the less strict encoder that does not encode all non-ASCII characters.
jsonSerializerOptions.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
jsonSerializerOptions = new JsonSerializerOptions(jsonSerializerOptions)
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
};
}

return new LocSystemTextJsonOutputFormatter(jsonSerializerOptions);
return new TestSystemTextJsonOutputFormatter(jsonSerializerOptions);
}

/// <summary>
/// Gets the <see cref="JsonSerializerOptions"/> used to configure the <see cref="JsonSerializer"/>.
/// </summary>
/// <remarks>
/// A single instance of <see cref="LocSystemTextJsonOutputFormatter"/> is used for all JSON formatting. Any
/// A single instance of <see cref="TestSystemTextJsonOutputFormatter"/> is used for all JSON formatting. Any
/// changes to the options will affect all output formatting.
/// </remarks>
public JsonSerializerOptions SerializerOptions { get; }
Expand All @@ -70,44 +70,50 @@ public sealed override async Task WriteResponseBodyAsync(OutputFormatterWriteCon

var httpContext = context.HttpContext;

var writeStream = GetWriteStream(httpContext, selectedEncoding);
try
// context.ObjectType reflects the declared model type when specified.
// For polymorphic scenarios where the user declares a return type, but returns a derived type,
// we want to serialize all the properties on the derived type. This keeps parity with
// the behavior you get when the user does not declare the return type and with Json.Net at least at the top level.
var objectType = context.Object?.GetType() ?? context.ObjectType ?? typeof(object);

var responseStream = httpContext.Response.Body;
if (selectedEncoding.CodePage == Encoding.UTF8.CodePage)
{
await JsonSerializer.SerializeAsync(responseStream, context.Object, objectType, SerializerOptions);
await responseStream.FlushAsync();
}
else
{
// context.ObjectType reflects the declared model type when specified.
// For polymorphic scenarios where the user declares a return type, but returns a derived type,
// we want to serialize all the properties on the derived type. This keeps parity with
// the behavior you get when the user does not declare the return type and with Json.Net at least at the top level.
var objectType = context.Object?.GetType() ?? context.ObjectType;
await JsonSerializer.SerializeAsync(writeStream, context.Object, objectType, SerializerOptions);
// JsonSerializer only emits UTF8 encoded output, but we need to write the response in the encoding specified by
// selectedEncoding
var transcodingStream = Encoding.CreateTranscodingStream(httpContext.Response.Body, selectedEncoding, Encoding.UTF8, leaveOpen: true);

// The transcoding streams use Encoders and Decoders that have internal buffers. We need to flush these
// when there is no more data to be written. Stream.FlushAsync isn't suitable since it's
// acceptable to Flush a Stream (multiple times) prior to completion.
if (writeStream is TranscodingWriteStream transcodingStream)
ExceptionDispatchInfo exceptionDispatchInfo = null;
try
{
await transcodingStream.FinalWriteAsync(CancellationToken.None);
await JsonSerializer.SerializeAsync(transcodingStream, context.Object, objectType, SerializerOptions);
await transcodingStream.FlushAsync();
}
await writeStream.FlushAsync();
}
finally
{
if (writeStream is TranscodingWriteStream transcodingStream)
catch (Exception ex)
{
await transcodingStream.DisposeAsync();
// TranscodingStream may write to the inner stream as part of it's disposal.
// We do not want this exception "ex" to be eclipsed by any exception encountered during the write. We will stash it and
// explicitly rethrow it during the finally block.
exceptionDispatchInfo = ExceptionDispatchInfo.Capture(ex);
}
}
}
finally
{
try
{
await transcodingStream.DisposeAsync();
}
catch when (exceptionDispatchInfo != null)
{
}

private Stream GetWriteStream(HttpContext httpContext, Encoding selectedEncoding)
{
if (selectedEncoding.CodePage == Encoding.UTF8.CodePage)
{
// JsonSerializer does not write a BOM. Therefore we do not have to handle it
// in any special way.
return httpContext.Response.Body;
exceptionDispatchInfo?.Throw();
}
}

return new TranscodingWriteStream(httpContext.Response.Body, selectedEncoding);
}
}
}

0 comments on commit eece75f

Please sign in to comment.