HTTPのPOSTメソッドの「Content-Type」について備忘録

HTTPでのやり取りを実装する中でContent-Typeについて調べたので、備忘録として残しておきます。

## Content-Typeとは

> Content-Type エンティティヘッダーは、リソースのメディア種別を示すために使用します。
> 要求においては (POST または PUT などで)、クライアントがサーバーにどのような種類のデータが実際に送られたかを伝えます。

[Content-Type - HTTP | MDN](https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Content-Type)

構文例：
`Content-Type: application/json;charset=utf8`
`Content-Type: application/x-www-form-urlencoded`
`Content-Type: text/html; charset=UTF-8`

実際のリクエストはこんな感じになる。

```
POST /test HTTP/1.1
Host: xxx.xx.xx.x
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
field1=value1&field2=value2
```


## GuzzleHttpを使ってPOSTする場合、Content-Typeに合わせたリクエスト送信オプション引数の設定が必要

Content-Typeが`application/x-www-form-urlencoded`なら`form_params`、
`application/json`なら`json`
をオプション引数に指定してあげる必要があります。

```php
// form_paramsの場合
$client->request('POST', '/post', [
    'form_params' => [
        'foo' => 'bar',
        'baz' => ['hi', 'there!']
    ]
]);

// jsonの場合
$client->request('PUT', '/put', [
    'json' => [
        'foo' => 'bar'
    ]
]);
```

[Request Options - Guzzle Documentation](https://docs.guzzlephp.org/en/stable/request-options.html)
[今時のPHP HTTPクライアントのGuzzleを使ってみた - Qiita](https://qiita.com/yousan/items/2a4d9eac82c77be8ba8b)

ここで正しいオプションを設定していないと、以下のようなエラーが出ます。（実際これが出て色々調べた）

```
Post request has been deprecated. Please use the "form_params" request option to send a application/x-www-form-urlencoded request, or the "multipart" request option to send a multipart/form-data request.
```


## `application/x-www-form-urlencoded`のレスポンスをPHPで解析
ついでに`application/x-www-form-urlencoded`のレスポンスの解析方法（PHP）についても事例を記録しておきます。（jsonは[json_decode](https://www.php.net/manual/ja/function.json-decode.php)するだけなので、ここでは省略します）

application/x-www-form-urlencodedでのレスポンスはURLエンコードされています。
まぁ、コンテンツタイプが「form」で「urlencoded」って書いてあるから、そりゃそうですね。

[パーセントエンコーディング](https://ja.wikipedia.org/wiki/%E3%83%91%E3%83%BC%E3%82%BB%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%82%B3%E3%83%BC%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0)

> HTTPのPOSTメソッドでWebフォームの文字列を送信する場合に、文字列はエンコードして送信される。その際の符号化方法はMIMEのContent-Typeがapplication/x-www-form-urlencodedで指定される。この符号化方法をURLエンコードと言うことがある。

というわけで、標準関数の[urldecode](https://www.php.net/manual/ja/function.urldecode.php)でデコードすればいけます。

```php

// レスポンス例
$response = 'error=0' . "\r\n" 
 . 'success=2' . "\r\n"
 . 'resultCode=100000123450201200%2C40008408345320370200';

// URLデコード
$decode = urldecode($response);
// 改行ごとに配列化
$responseList = explode("\r\n", $decode);

var_dump($responseList);
// array(3) {
//   [0] =>
//   string(7) "error=0"
//   [1] =>
//   string(9) "success=2"
//   [2] =>
//   string(50) "resultCode=100000123450201200,40008408345320370200"
// }

$result = [];
foreach ($responseList as $row) {
    // 各行ごとに"="で分割し、いい感じの配列にする。
    $rowArray = explode("=", $row);
    $result[$rowArray[0]] = $rowArray[1];
}

var_dump($result);
// array(3) {
//   ["error"]=>
//   string(1) "0"
//   ["success"]=>
//   string(1) "2"
//   ["resultCode"]=>
//   string(39) "100000123450201200,40008408345320370200"
// }
```

## その他参考
* [PHP GuzzleHttp. How to make a post request with params?](https://stackoverflow.com/questions/27825667/php-guzzlehttp-how-to-make-a-post-request-with-params)
* [URLエンコードについておさらいしてみた - Qiita](https://qiita.com/sisisin/items/3efeb9420cf77a48135d)