Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Handling json pretty print param in ASP.NET Core API application

This question is almost verbatim what is being asked on this stackoverflow question

But I'd like to do it in a ASP.NET Core Api. I'm planning on implementing it as a a header instead of in the query string. I'm struggling with changing the Json SerializerSettings that has been set in the ConfigureServices method.

services.AddMvc(config =>
        {
            config.Filters.Add(new WebApiRequireHttps());                
        })
            .AddJsonOptions(options =>
            {
                options.SerializerSettings.Formatting = Formatting.Indented;
            });

What I'm trying to accomplish is change the Serializer's settings to use Formatting.None when I've set a header like X-MyApi-Pretty: false.

I'm trying to create my own ActionFIlterAttribute like in the aforementioned question. Microsoft.AspNetCore.Mvc.Filters uses ActionExecutedContext which doesn't seem to have an obvious equivalent to ActionContext.RequestContext.Configuration.Formatters.JsonFormatter.

Is there an equivalent or am I just going about this all wrong? My google-fu is really failing me on this and I feel like I'm missing something really obvious.

like image 911
Corey Avatar asked May 12 '17 14:05

Corey


1 Answers

If you want it to be global, you can register a custom output formatter. Here's a working example I made. You're free to use.

It works by reading a header named "jsonformat" for a named json serializer setting, such as one named "pretty". If no header is present, or the header is invalid, it falls back to the default json serializer settings. This might be a cleaner approach if you want it available globally - no action filters required.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;

...

public class NamedFormatJsonOutputFormatter : JsonOutputFormatter
{
    private readonly IDictionary<string, JsonSerializerSettings> _customJsonSettings;
    private readonly IDictionary<string, JsonSerializer> _customSerializers;
    public NamedFormatJsonOutputFormatter(JsonSerializerSettings defaultSerializerSettings, IDictionary<string, JsonSerializerSettings> customJsonSettings, ArrayPool<char> charPool) : base (defaultSerializerSettings, charPool)
    {
        _customJsonSettings = customJsonSettings;
        _customSerializers = new Dictionary<string, JsonSerializer>();
    }
    protected virtual JsonSerializer CreateCustomJsonSerializer(string serializerName)
    {
        JsonSerializer outputSerializer;
        var exists = _customSerializers.TryGetValue(serializerName, out outputSerializer);
        if (!exists)
        {
            var settings = _customJsonSettings[serializerName];
            outputSerializer = JsonSerializer.Create(settings);
            _customSerializers[serializerName] = outputSerializer;
        }
        return _customSerializers[serializerName];
    }
    public void WriteObjectWithNamedSerializer(TextWriter writer, string serializerName, object value)
    {
        if (writer == null)
        {
            throw new ArgumentNullException(nameof(writer));
        }

        using (var jsonWriter = CreateJsonWriter(writer))
        {
            var jsonSerializer = CreateCustomJsonSerializer(serializerName);
            jsonSerializer.Serialize(jsonWriter, value);
        }
    }
    public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
    {
        var jsonSerializerNameHeader = context.HttpContext.Request.Headers.FirstOrDefault(h => h.Key == "jsonformat" && h.Value.Intersect(_customJsonSettings.Keys).Any());
        if (jsonSerializerNameHeader.Equals(default(KeyValuePair<string, StringValues>)))
        {
            await base.WriteResponseBodyAsync(context, selectedEncoding);
            return;
        }
        var jsonFormatName = jsonSerializerNameHeader.Value.FirstOrDefault();

        var response = context.HttpContext.Response;
        using (var writer = context.WriterFactory(response.Body, selectedEncoding))
        {
            WriteObjectWithNamedSerializer(writer, jsonFormatName, context.Object);
            await writer.FlushAsync();
        }
    }
}

Then register it in your services.AddMvc

using System.Buffers;
using Microsoft.AspNetCore.Mvc.Formatters;

...

services.AddMvc(options =>
{
    options.OutputFormatters.RemoveType<JsonOutputFormatter>();
    options.OutputFormatters.Add(new NamedFormatJsonOutputFormatter(new JsonSerializerSettings(),

        new Dictionary<string, JsonSerializerSettings>()
        {
            {
                "pretty", new JsonSerializerSettings()
                {
                    Formatting = Formatting.Indented
                }
            }
        }, ArrayPool<char>.Shared));
});
like image 126
kg743 Avatar answered Nov 05 '22 09:11

kg743