おーみんブログ

C#, ASP.NET Core, Unityが大好きです。

ASP.NET Core WebAPIでCustom Formatterを利用してCSVとJSONの出力を切り替えるサンプル。

はじめに

下記の記事にてASP.NET Core WebAPIでCSVJSONの出力を切り替えるサンプル記事を作成しましたが、お客様先(常駐先)の上司にCustom Formatterの存在を教えてもらったので勉強として色々調査しながら実装してみました!

oooomincrypto.hatenadiary.jp

CSV出力

以下を参考にTextOutputFormatterを継承してCsvOutputFormatクラスを作成します。

stackoverflow.com

docs.microsoft.com

public class CsvOutputFormat : TextOutputFormatter
{
    private static readonly string[] Summaries = new[]
    {
        "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
    };

    public CsvOutputFormat()
    {
        // MediaTypeの情報を記載します。
        SupportedMediaTypes.Add(Microsoft.Net.Http.Headers.MediaTypeHeaderValue.Parse("text/csv"));

        SupportedEncodings.Add(Encoding.UTF8);
        SupportedEncodings.Add(Encoding.Unicode);
    }

    protected override bool CanWriteType(Type type)
    {
        // IEnumerable<WeatherForecast>型が入ってくるかチェック
        return typeof(IEnumerable<WeatherForecast>).IsAssignableFrom(type);
    }

    public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
    {
        // 元々のCSV返却処理
        var response = context.HttpContext.Response;

        var rng = new Random();
        using var memory = new MemoryStream();
        using var writer = new StreamWriter(memory);
        using var csv = new CsvWriter(writer, CultureInfo.InvariantCulture);
        var weatherForecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = rng.Next(-20, 55),
            Summary = Summaries[rng.Next(Summaries.Length)]
        }).ToList();

        csv.WriteRecords(weatherForecasts);
        writer.Flush();

        return response.Body.WriteAsync(memory.ToArray()).AsTask();
    }

    public override void WriteResponseHeaders(OutputFormatterWriteContext context)
    {
        // レスポンスで返却されるCSVファイルをダウンロードできるようにする
        context.HttpContext.Response.Headers["Content-Disposition"] =
        new ContentDispositionHeaderValue("attachment")
        {
            FileName = "Test.csv"
        }.ToString();
    }
}
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.OutputFormatters.Insert(0, new CsvOutputFormat());
    });
    //省略
}

Json出力

Json出力部分は元々のGet()に記載した通りです。

[Produces("application/json", "text/csv")]
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
    var rng = new Random();
    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = rng.Next(-20, 55),
        Summary = Summaries[rng.Next(Summaries.Length)]
    }).ToList();
}

JSONについてはoptions.OutputFormattersの中にデフォルトで以下のSystemTextJsonOutputFormatterが入っています。

https://docs.microsoft.com/ja-JP/dotnet/api/microsoft.aspnetcore.mvc.formatters.systemtextjsonoutputformatter?view=aspnetcore-5.0

結果

どちらも前回の記事同様、正常に出力されていることが分かります。

application/json

applicationjson1.png

text/csv

textcsv1.png

おわりに

今回はCustom Formatterについて調査してみましたが、一つ一つが何をやっているのかを追うと結構難しかったです(;´∀`) 前回の記事ではAcceptヘッダーを見て場合分けする処理を明記していましたが、こちらの方がより処理も分割されていますしより良い感じがするのでしっかり理解を深めていきたいと思います。