157 lines
5.3 KiB
C#
157 lines
5.3 KiB
C#
// Copyright (c) .NET Foundation. All rights reserved.
|
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
|
|
using System;
|
|
using System.Buffers;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Microsoft.AspNet.Mvc.Formatters.Json.Internal;
|
|
using Microsoft.AspNet.Mvc.Internal;
|
|
using Newtonsoft.Json;
|
|
|
|
namespace Microsoft.AspNet.Mvc.Formatters
|
|
{
|
|
/// <summary>
|
|
/// An output formatter that specializes in writing JSON content.
|
|
/// </summary>
|
|
public class JsonOutputFormatter : OutputFormatter
|
|
{
|
|
private readonly IArrayPool<char> _charPool;
|
|
|
|
private JsonSerializerSettings _serializerSettings;
|
|
|
|
// Perf: JsonSerializers are relatively expensive to create, and are thread safe. We cache
|
|
// the serializer and invalidate it when the settings change.
|
|
private JsonSerializer _serializer;
|
|
|
|
public JsonOutputFormatter()
|
|
: this(SerializerSettingsProvider.CreateSerializerSettings(), ArrayPool<char>.Shared)
|
|
{
|
|
}
|
|
|
|
public JsonOutputFormatter(JsonSerializerSettings serializerSettings)
|
|
: this(serializerSettings, ArrayPool<char>.Shared)
|
|
{
|
|
}
|
|
|
|
public JsonOutputFormatter(JsonSerializerSettings serializerSettings, ArrayPool<char> charPool)
|
|
{
|
|
if (serializerSettings == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(serializerSettings));
|
|
}
|
|
|
|
if (charPool == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(charPool));
|
|
}
|
|
|
|
_serializerSettings = serializerSettings;
|
|
_charPool = new JsonArrayPool<char>(charPool);
|
|
|
|
SupportedEncodings.Add(Encoding.UTF8);
|
|
SupportedEncodings.Add(Encoding.Unicode);
|
|
SupportedMediaTypes.Add(MediaTypeHeaderValues.ApplicationJson);
|
|
SupportedMediaTypes.Add(MediaTypeHeaderValues.TextJson);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the <see cref="JsonSerializerSettings"/> used to configure the <see cref="JsonSerializer"/>.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Any modifications to the <see cref="JsonSerializerSettings"/> object after this
|
|
/// <see cref="JsonOutputFormatter"/> has been used will have no effect.
|
|
/// </remarks>
|
|
public JsonSerializerSettings SerializerSettings
|
|
{
|
|
get
|
|
{
|
|
return _serializerSettings;
|
|
}
|
|
set
|
|
{
|
|
if (value == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(value));
|
|
}
|
|
|
|
_serializerSettings = value;
|
|
|
|
// If the settings change, then invalidate the cached serializer.
|
|
_serializer = null;
|
|
}
|
|
}
|
|
|
|
public void WriteObject(TextWriter writer, object value)
|
|
{
|
|
if (writer == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(writer));
|
|
}
|
|
|
|
using (var jsonWriter = CreateJsonWriter(writer))
|
|
{
|
|
var jsonSerializer = CreateJsonSerializer();
|
|
jsonSerializer.Serialize(jsonWriter, value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called during serialization to create the <see cref="JsonWriter"/>.
|
|
/// </summary>
|
|
/// <param name="writer">The <see cref="TextWriter"/> used to write.</param>
|
|
/// <returns>The <see cref="JsonWriter"/> used during serialization.</returns>
|
|
protected virtual JsonWriter CreateJsonWriter(TextWriter writer)
|
|
{
|
|
if (writer == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(writer));
|
|
}
|
|
|
|
var jsonWriter = new JsonTextWriter(writer)
|
|
{
|
|
ArrayPool = _charPool,
|
|
CloseOutput = false,
|
|
};
|
|
|
|
return jsonWriter;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Called during serialization to create the <see cref="JsonSerializer"/>.
|
|
/// </summary>
|
|
/// <returns>The <see cref="JsonSerializer"/> used during serialization and deserialization.</returns>
|
|
protected virtual JsonSerializer CreateJsonSerializer()
|
|
{
|
|
if (_serializer == null)
|
|
{
|
|
_serializer = JsonSerializer.Create(SerializerSettings);
|
|
}
|
|
|
|
return _serializer;
|
|
}
|
|
|
|
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context)
|
|
{
|
|
if (context == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(context));
|
|
}
|
|
|
|
var response = context.HttpContext.Response;
|
|
var selectedEncoding = context.ContentType?.Encoding ?? Encoding.UTF8;
|
|
|
|
using (var writer = context.WriterFactory(response.Body, selectedEncoding))
|
|
{
|
|
WriteObject(writer, context.Object);
|
|
|
|
// Perf: call FlushAsync to call WriteAsync on the stream with any content left in the TextWriter's
|
|
// buffers. This is better than just letting dispose handle it (which would result in a synchronous
|
|
// write).
|
|
await writer.FlushAsync();
|
|
}
|
|
}
|
|
}
|
|
}
|