ASP.NET Webアプリケーションで text/plain レスポンスで日本語(マルチバイト)の文字列を返すと表示が文字化けする - ASP.NET

ASP.NET Webアプリケーションで text/plain レスポンスで日本語(マルチバイト)の文字列を返すと表示が文字化けする現象について紹介します。

概要

ASP.NET WebアプリケーションでHTMLではなく text/plain のテキスト形式のレスポンスを返すWebアプリケーションを作成し、 レスポンスに日本語の文字列(マルチバイト文字列)が含まれると、日本語の表示が文字化けして表示されることがあります。

動作の確認

ASP.NET Webアプリケーション(.NET 5)を作成し、下記のコードを記述します。
処理はシンプルでASP.NET Webアプリケーションのアプリケーションルートにアクセスすると、 "Hello World!\r\n" と "こんにちは、ASP.NETの世界へ!\r\n" の文字列を返すアプリケーションです。
文字列を返すだけのため、レスポンスはHTMLではなく、無指定、あるいは text/plain となります。
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace TextPlainResponse
{
  public class Startup
  {
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      }

      app.UseRouting();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapGet("/", async context =>
              {
                await context.Response.WriteAsync("Hello World!\r\n");
                await context.Response.WriteAsync("こんにちは、ASP.NETの世界へ!\r\n");
              });
      });
    }
  }
}

上記のプロジェクトを実行します。Webブラウザを起動しアプリケーションルートのURLにアクセスします。
下図の画面が表示されます。日本語の文字列「こんにちは、ASP.NETの世界へ!」が文字化けして表示されることが確認できます。

原因

ASP.NET WebアプリケーションのWriteAsyncの出力はデフォルトでは、UTF-8 で出力されます。 一方、Google ChromeやMicrosoft Edge などのWebブラウザでは、レスポンスの指定がない場合、コンテンツをShift-JISとして扱うため、 UTF-8のレスポンスをShift-JISとして画面に表示する動作になり、文字化けが発生します。

上記のプロジェクトのレスポンスヘッダを確認すると、content-typeヘッダはなく、何も指定が無いことが確認できます。

対処法

以下の対処法があります。
  1. content-type ヘッダを設定しレスポンスを UTF-8 エンコーディングされているコンテンツとして処理する
  2. ASP.NET Webアプリケーションの日本語(マルチバイト文字列)の出力をShift-JISにする

対策後のプログラム : Content-Type ヘッダを設定

レスポンスヘッダに content-type フィールドを追加し、MIME Type, charset を指定してUTF-8のコンテンツであることを 明示して文字化けしないようにする対処方法です。

コード

以下のコードを記述します。
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace TextPlainResponse
{
  public class Startup
  {
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      }

      app.UseRouting();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapGet("/", async context =>
              {
                context.Response.ContentType = "text/plain; charset=utf-8";
                await context.Response.WriteAsync("Hello World!\r\n");
                await context.Response.WriteAsync("こんにちは、ASP.NETの世界へ!\r\n");
              });
      });
    }
  }
}

解説

以下のコードにより、レスポンスヘッダの content-type ヘッダにMIME Typeとcharset(エンコーディング)を設定しています。 このコードにより、レスポンスコンテンツをUTF-8でエンコーディングされた文字列として処理します。
  context.Response.ContentType = "text/plain; charset=utf-8";

実行結果

上記のプロジェクト実行し、Webブラウザでアプリケーションルートにアクセスします。 下図の画面が表示されます。文字化けせずにレスポンスの文字列が表示できています。


レスポンスヘッダの値を確認します。レスポンスヘッダに content-type ヘッダがあります。 ContentType の値は text/plain; charset=utf-8 が設定されており、レスポンスのエンコーディングはUTF-8であることが明示されています。 この設定により、文字化けせずに日本語の文字列(マルチバイト文字列)が表示されます。

対策後のプログラム : Shift-JIS のレスポンスを返す

ASP.NET アプリケーションのレスポンスの日本語(マルチバイト)文字列をShift-JISとしてレスポンスする方法です。

コード

Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace TextPlainResponse
{
  public class Startup
  {
    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
      if (env.IsDevelopment()) {
        app.UseDeveloperExceptionPage();
      }

      app.UseRouting();

      app.UseEndpoints(endpoints =>
      {
        endpoints.MapGet("/", async context =>
              {
                System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
                System.Text.Encoding sjisEncoding = System.Text.Encoding.GetEncoding("SHift-JIS");

                await context.Response.WriteAsync("Hello World!\r\n");
                await context.Response.WriteAsync("こんにちは、ASP.NETの世界へ!\r\n", sjisEncoding);
              });
      });
    }
  }
}

解説

Shift-JISのEncoding オブジェクトを取得します。.NET Framework の場合は、GetEncoding のみでEncodingオブジェクトを取得できましたが、 .NET 5 (.NET Core)の場合は、エンコーディングプロバイダを追加しておく必要があります。詳しくはこちらの記事を参照してください。
RegisterProvider() メソッドを呼び出し、CodePagesEncodingProvider を追加した後に、GetEncoding("Shift-JIS") メソッドを呼び出すと Shift-JISのEncoding オブジェクトを取得できます。
  System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
  System.Text.Encoding sjisEncoding = System.Text.Encoding.GetEncoding("SHift-JIS");

レスポンスの出力は先のプログラムと同様です。マルチバイト文字列の出力の WriteAsync() メソッドには第二引数にエンコードするEncodingオブジェクトを与えています。 Shift-JISのEncodingオブ絵ジェクトを与えているため、日本語の文字列はShift-JISエンコーディングされてクライアントに送信されます。
  await context.Response.WriteAsync("Hello World!\r\n");
  await context.Response.WriteAsync("こんにちは、ASP.NETの世界へ!\r\n", sjisEncoding);

表示結果

上記のプロジェクトを実行し、WebアプリケーションルートのURLにアクセスします。
下図の画面が表示されます。文字化けせずにレスポンスの文字列が表示できています。


レスポンスヘッダの値を確認します。レスポンスヘッダに content-type ヘッダはありません。
レスポンスコンテンツのエンコーディングがShift-JISのため、文字化けせずに画面に表示されます。

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2021-07-16
作成日: 2021-07-15
iPentec all rights reserverd.