困っていたこと
PHPでGoogle Maps PlatformのPlaces APIを使うため、google/apiclientを使って近所の店舗を検索しようとしていた。 最初に下記のようなコードを書いたのだが、FieldMask is a required parameter.という400エラーが返ってくる。
$client = new Google\Client(); $client->setDeveloperKey($_ENV['GOOGLE_API_KEY']); $mapPlacesApi = new Google\Service\MapsPlaces($client); $request = new Google\Service\MapsPlaces\GoogleMapsPlacesV1SearchTextRequest(); $request->setLanguageCode('ja'); $request->setTextQuery('マクドナルド'); $result = $mapPlacesApi->places->searchText($request);
このエラーを解消するのに時間が掛かってしまったので、備忘録を残しておく
原因・解決方法
レスポンスに含めるべきフィールドを指定していないことが原因なので、レスポンスに含めたいフィールドをfieldsパラメータとして渡してあげる必要がある。*1
動作する例
$client = new Google\Client(); $client->setDeveloperKey($_ENV['GOOGLE_API_KEY']); $mapPlacesApi = new Google\Service\MapsPlaces($client); $request = new Google\Service\MapsPlaces\GoogleMapsPlacesV1SearchTextRequest(); $request->setLanguageCode('ja'); $request->setTextQuery('マクドナルド'); // 取得したいフィールドをsearchTextの第2引数として渡す $fields = ['places.id', 'places.displayName']; $result = $mapPlacesApi->places->searchText($request, ['fields' => implode(',', $fields)]);
ここで指定するfieldsはFieldMaskに記載されているフィールド名のリストである。
調べたこと
この解決方法に行き着くまでの経緯も軽く書いておくと、まず、エラーの全文は下記のようなメッセージになっている。
FieldMask is a required parameter. See https://cloud.google.com/apis/docs/system-parameters on how to provide it. As an example, you can set the header 'X-Goog-FieldMask' to value 'places.displayName', 'places.id' to ask for the display name and the place id of a place. You can also set the value to '*' in manual testing to get all the available response fields.",
この時点で、レスポンスとして返すべきフィールドが指定されていないためエラーになっていることが分かる。
しかし、エラーメッセージ内のURLを参照してもX-Goog-FieldMaskヘッダーを付与する必要があるという情報しかなく、このページを見てもSDKでどのようにヘッダーを付与する方法や、取得できるフィールドの種類に関する情報が何も得られない。
cloud.google.com
複数言語のSDKでissueが起票されているほど、ヘッダーの付与方法についてはとても分かりにくいことが調べていく中で分かった。
X-Goog-FieldMaskヘッダーを付与する方法を調べる
まず、エラーメッセージに従ってヘッダーを付与する方法を調べるためSDKのコードを読み進めた。 しかし、SDKでリクエストを組み立てている \Google\Service\Resource::call の中で直接new Request()とインスタンスを生成していて、ヘッダーを付与することは難しそうに見えた 。 *2
他の方法でfieldsを渡す方法を模索する
SDKを使わずに、Guzzleなどで独自にAPIリクエストを組み立てようかとドキュメントをあさっているときに、たまたまFieldMaskの説明が目に入った。
FieldMask
レスポンス フィールド マスクを作成して、レスポンスで返すフィールドのリストを指定します。URL パラメータ $fields または fields を使用するか、HTTP ヘッダー X-Goog-FieldMask を使用して、レスポンス フィールド マスクをメソッドに渡します。レスポンスには、返されるフィールドのデフォルト リストはありません。フィールド マスクを省略すると、メソッドはエラーを返します。
X-Goog-FieldMaskヘッダーではなく、fieldsURLパラメータでも良いらしい...
しかし、必須パラメータであるにも関わらず$request->setFields()なんてメソッドは用意されていないのは何度も確認している。
そこで、さらにSDKのコードを読み進めたところ、第2引数の$optParamsに指定すれば良いということが分かった。
補足: SDKがどのようにfieldsパラメータを許容しているか
これも少しややこしい実装になっていて、SDK側で$optParamsに指定できるパラメータはバリデーションが設けられており、存在しないパラメータを指定するとリクエストを送る以前にService parameter unknownのようなエラーになってしまう。
searchText APIに関してはGoogle\Service\MapsPlacesクラスを見るとオプショナルなパラメータ自体が存在しないように見える。*3
'searchText' => [ 'path' => 'v1/places:searchText', 'httpMethod' => 'POST', 'parameters' => [], ],
しかし、Google\Service\Resourceの$stackParametersに定義があるため、有効なフィールドであることが分かった。*4
private $stackParameters = [
'alt' => ['type' => 'string', 'location' => 'query'],
'fields' => ['type' => 'string', 'location' => 'query'],
'trace' => ['type' => 'string', 'location' => 'query'],
// 略
];
*1:このコードでは検索地点の経度/緯度情報がないのでIPから推測された地点を基準とした結果が返ります
*2:https://github.com/googleapis/google-api-php-client/blob/7e79f3d7be4811f760e19cc4a2c558e04196ec1d/src/Service/Resource.php#L206-L248
*3:https://github.com/googleapis/google-api-php-client-services/blob/235e6a45ecafd77accc102b5ab6d529aab54da23/src/MapsPlaces.php#L113-L117
*4:https://github.com/googleapis/google-api-php-client/blob/main/src/Service/Resource.php#L35-L37