2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ブラックボックスの罠 Guzzlehttpで解決しなかった文字化けが、cURLにしたらサクッと解決した

Posted at

はじめに

APIを使う側の場合、サンプルコードなどがあれば、まずはそれに忠実な操作、処理をして疎通を確認する。

ライブラリは便利だが、ブラックボックスになる面もあるのでトラブルの原因把握や問題解決から遠くなる危険がある。
ソースが公開されているのなら、それを辿るがよろし。

何が起こったか?

某決済業者のAPIを使い銀行振込用のバーチャル口座のリクエストを送った時に、全角文字が含まれているとその部分だけ文字化けが発生した。
なお、クレジットカード払いなどの別手段では発生しなかった。

参考:当方サーバ側環境

  • PHP7.X系
  • Laravel5.X系
  • guzzlehttp6.5.2
  • システム内のデフォルトの文字コードはUTF-8

PHPやLaravelそのものは関係なくて、guzzleの中の問題(断言)

問題調査で発生したこと

途方に暮れる

文字コードをSJISに変換する必要がある
→変換しても、文字化けた後の内容が変わっただけで根本的な解決にはならなかった。

リクエストを送るときのContent-Typeが問題 
→ Postmanなどの、API送信ツールを使ったが、文字化けしていた。

そのほか、URLエンコードした値を送信など様々な事を試みたがダメだった。

なお、業者がサンプルで提供している(本案件では採用しない)決済モジュールを動かしてみたら無事文字化けしなかった。

cURLで実装してみたら、問題なく送信できた!

決済モジュールのコードを辿ってみたら、全角文字列はSJISに変換する。
curl_setoptで、様々な設定をしたのちリクエストを送信。

結局、いかにもありそうなcURLのサンプルコードを書いて動かしたら成功した。

cURLでリクエストを送る

//前提:こんな感じのリクエスト値が存在する(実運用時には、他のメソッドで加工するケースが多いと思います)

$params = [
    'access_code' => 'unique_access_code',
    'price' => '3980',
    'customer_name' => mb_convert_encoding('バカ殿様','SJIS','UTF-8')
];

//POSTする値を
$post_fields = http_build_query($params);

// APIのリクエスト先URL。各自の環境に置き換えて考えてください
$request_url = "https://example.com/path/to/api/receiver";


//まず、オブジェクトを作る
$curl = curl_init();

//リクエスト形式、戻り値の存在、リクエスト先などを設定する。
//URLは適宜考えてください

curl_setopt($curl,CURLOPT_URL,$request_url);
curl_setopt($curl,CURLOPT_POST,true);
curl_setopt($curl,CURLOPT_RETURNNUMBER,true);

//リクエスト先の仕様にそろえて、URLエンコード済の値をセットする
curl_setopt($curl,CURLOPT_POSTFIELDS,$post_fields);

//ここから下、cURLのリクエスト送信とレスポンス

$response = curl_exec( $curl );
$curlinfo = curl_getinfo( $curl );
curl_close( $curl );




顧客名は、時事ネタです(合掌)

犯人は(たぶん)お前だ!

guzzlehttp/guzzle/src/Client.php

applyOptionsメソッド内にてhttp_build_queryを使っていたが
ここでクエリを編集する部分に問題があるようだ。

guzzlehttp/guzzle/src/Client.php

//
        if (isset($options['query'])) {
            $value = $options['query'];
            if (is_array($value)) {
                $value = http_build_query($value, null, '&', PHP_QUERY_RFC3986);//この行に問題あり
            }
            if (!is_string($value)) {
                throw new \InvalidArgumentException('query must be a string or array');
            }
            $modify['query'] = $value;
            unset($options['query']);
        }



PHPリファレンスのhttp_build_queryのページによれば 4つ目の引数enc_typeは

デフォルトでは PHP_QUERY_RFC1738つまり、RFC1738 に従ってエンコードされる。でよかったにも関わらず 上記ソースの抜粋のようにPHP_QUERY_RFC3986でエンコードされていた…これが原因のようだ。

参考

PHP:http_build_query

2
1
0

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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?