LoginSignup
4

More than 5 years have passed since last update.

WebAPIでmultipart/form-dataを使うときは非同期メソッドで

Posted at

はじめに

バックエンドのシステムを利用していると、multipart/form-dataでリクエストを送る/受け付けるということは滅多にないと思います。
しかし、あるプロジェクトで既存システムが何故かマルチパートでXMLを送るように作られてしまっていたために、Web APIでどうしてもmultipart/form-dataを使う必要がありました。
そのとき、非同期メソッドの扱いで罠に嵌った話です。

Web APIでmultipart/form-dataを扱うには

マルチパート自体は下記のように簡単に扱えます。

public void Post(HttpRequestMessage request)
{
    if (request.Content.IsMimeMultipartContent())

        MultipartMemoryStreamProvider provider = request.Content.ReadAsMultipartAsync().Result;

        foreach(var content in  provider.Contents)
        {
            // 各Contentを処理
        }
    }
}

マルチパートの読み込みで処理がスタックしてしまう

先ほどのソースコードはPostを同期メソッドで作成していました。
この状態で運営をしてみたところ、ReadAsMultipartAsync()で処理がスタックするという障害が発生してしまいました。

色々調べて、行き着いたのがこちらの記事です。
Don't Block on Async Code

One other important point: an ASP.NET request context is not tied to a specific thread (like the UI context is), but it does only allow one thread in at a time.

ということで、

  • ASP.NETのRequestコンテキストを使用しているPostメソッド内で、非同期メソッドAを同期的に呼び出す
  • 非同期メソッドAの中で、Requestコンテキストに関連する非同期メソッドを呼び出す

という処理が入るとデッドロックが発生するようです。
ReadAsMultipartAsync()も内部でさらに非同期メソッドを呼んでいるのではないかと・・・

非同期メソッドにすれば解決する

POSTメソッド自体を非同期メソッドに変更し、ReadAsMultipartAsync()をロックしないようにすれば、
前述のデッドロックは発生しなくなります。

public async Task PostAsync(HttpRequestMessage request)
{
    if (request.Content.IsMimeMultipartContent())

        MultipartMemoryStreamProvider provider = await request.Content.ReadAsMultipartAsync();

        foreach(var content in  provider.Contents)
        {
            // 各Contentを処理
        }
    }
}

まとめ

Web APIは極力、非同期処理化を進めましょう。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4