こんにちは、ギフティでエンジニアをしている @megane42 です。
先日、 faraday_middleware gem のバージョン 1.2.1 がリリースされました。さっそく手元でアップデートして動作確認したところ、一部の外部 API との通信だけが正しく動作しなくなってしまいました。この記事では、その原因と対処法をメモしています。
なお、faraday_middleware gem は執筆時点ですでに deprecated になっていますが、当該バージョンだけでも120 万回以上ダウンロードされており、今でも一定数の使用者がいるようです。
TL;DR
下記の条件にマッチする状態で faraday_middleware を v1.2.1 にアップデートすると、該当する外部 API のレスポンスボディの JSON がパースされなくなってしまいます。
- JSON response middleware を使っている
- faraday を使って外部 API を呼び出しているが、そのレスポンスヘッダに
Content-Type: application/json
が 含まれていない
その場合、JSON response middleware の content_type
オプションとして、下記のように空配列を渡すことで問題を解消できます。
Faraday.new(...) do |f| f.response :json, content_type: [] end
そもそも faraday の middleware とは
faraday には middleware という仕組みがあります。middleware を使うと、リクエストを投げる前やレスポンスを受けた後に、何らかの処理を挟むことができます。例えば、
- リクエストヘッダーに
Authorization
ヘッダーを追加する Authorization middleware - レスポンスステータスが 4xx または 5xx だったら例外を投げる RaiseError middleware
- レスポンスボディの JSON をパースして ruby の Hash に変換してくれる JSON response middleware
等です。今回のトラブルは、3 つ目の JSON response middleware にまつわるものです。
そもそも faraday_middleware gem とは
faraday_middleware gem は、さまざまな middleware を集めたコレクション gem です。件の JSON response middleware は、元々は faraday_middleware gem のコレクションの一部でしたが、今は faraday 本体に標準搭載されています。
faradamy_middleware gem v1.2.1 で変わったこと
faraday_middleware gem v1.2.1 からは、たとえ faraday_middleware gem をプロジェクトの Gemfile に書いてバンドルしていても、faraday 本体に標準搭載されている方の JSON middleware が使われるようになりました。
middleware の定義場所が変わっただけで、挙動は互換性がある・・・と思いきや、実は細かい挙動の違いが存在します。faraday 標準搭載の JSON response middleware には、レスポンスヘッダの Content-Type
を見て、JSON パースを行うべきかどうか判断する処理が存在します。つまり、レスポンスボディを JSON で返しているのに Content-Type
が application/json
になっていないような外部 API が存在した場合、v1.2.1 へのアップデート後からはその API のレスポンスボディの JSON がパースされなくなってしまいます。そして運悪く、私の担当プロダクトの連携先にそんな API があった...というわけです。
対処法
ありがたいことに、実はこの事象はすでに issue が報告されていました。この issue に対するメンテナからのコメントによると、JSON response middleware を利用する際に content_type
オプションに空配列を渡すと、Content-Type のチェック処理をバイパスできると書かれています。具体的には以下のような実装をすれば OK です。
Faraday.new(...) do |f| f.response :json, content_type: [] end
上記コメントから該当箇所の実装へのリンクも貼られていますが、たしかに @content_types.empty?
が true のときはチェック処理がバイパスされそうです。とはいえやや hacky な印象があるので、コードコメントで意図を補っておきたいですね。
まとめ
以上、faraday_middleware gem を v1.2.1 に上げたときのちょっとしたトラブルの話でした。このトラブルは v1.2.1 へのアップデート時だけでなく、JSON middleware を faraday 本体に標準搭載されているものに切り替えるタイミングであればいつでも発生するはずです。冒頭で書いたように faraday_middleware gem 自体は deprecated なので、今後プロジェクトの Gemfile から削除することがあるかもしれませんが、その際に faraday_middleware gem が v1.2.1 未満だった場合は同じトラブルを踏む可能性があるのでご注意ください。
ところで、今回私が迅速に原因を特定できたのは、リリース直後に issue を報告してくれた人と、その issue に素早くかつ的確にレスポンスをしてくれたメンテナのおかげです。ギフティが掲げるエンジニアカルチャーのひとつに「知見を贈りあう」というものがありますが、私も OSS コミュニティから take を受け取るばかりではなく、少しでも give を実践できるように頑張っていきたいです。