10
5

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 5 years have passed since last update.

Cloud Functions for FirebaseでRequest has invalid method. GETかBad Request, INVALID_ARGUMENTならこれかも?

Last updated at Posted at 2019-12-22

Overview

私はFunctionsからBigQueryに問い合わせて、結果をただ返したかっただけだった。
しかし、"message:"Bad Request", status:"INVALID_ARGUMENT"の闇に遭難することになる。
今回はその中で、FunctionsのログにRequest has invalid method. GETと出るケースを扱う。
Bad Requestは様々な理由が考えられるため、Functionsのログをみて該当するケースか確認してほしい。
(巨大なオブジェクトが出力されているため冒頭のエラーを見るのはなかなか大変だが)

Target reader

Request has invalid method. GETが解決できないユーザー

Body

いきなり結論

結論的には似たようなケースを扱ったことがあるのでそこから抜粋する。
https://qiita.com/qrusadorz/items/40234ac0b5c5c2315cad

FirebaseのCloud Functionsには2つの関数があります。

  1. functions.https.onCall
  2. functions.https.onRequest

1はSDKが用意されているAndroid,iOS,Javascriptから呼び出されるタイプ。
https://firebase.google.com/docs/functions/callable?hl=ja
2はSDKがない場合、汎用のWebAPIとして作成するタイプ。
https://firebase.google.com/docs/functions/http-events?hl=ja

functions.https.onCallで作っている場合、汎用のWebAPIとは異なりhttps.onCall のプロトコルの仕様に従わなくてはならない。
どういう縛りがあるのか、公式ドキュメントから仕様を抜粋する。
https://firebase.google.com/docs/functions/callable-reference?hl=ja

リクエストの形式: ヘッダー
呼び出し可能なトリガー エンドポイントへの HTTP リクエストは、次のヘッダーが含まれる POST にする必要があります。

必須: Content-Type: application/json
オプション: Authorization: Bearer
オプション: Firebase-Instance-ID-Token:

その他のヘッダーが含まれる場合は、下記のレスポンスに関するドキュメントで説明されているように、リクエストは拒否されます。

まずヘッダーにapplication/jsonが必須。
日本語がおかしいが、リクエストはGETではなくPOSTでなくてはならない。

リクエスト本文
HTTP リクエストの本文は、次のいずれかのフィールドが含まれる JSON オブジェクトにする必要があります。

必須: data - 関数に渡される引数です。これには任意の有効な JSON 値を使用できます。これは、以下で説明するシリアル化形式に従って、>JavaScript のネイティブ型に自動的にデコードされます。
リクエストに他のフィールドが存在する場合、バックエンドはそのリクエストの形式を不正とみなし、リクエストは拒否されます。

そして本体にはdataフィールドが必須。

私がどうやってAPIにアクセスしていたか、確認しよう。
serveコマンドでローカルサーバーを立てる。
準備ができると、以下のようなログがVSCodeのターミナルに出力される。(Function名はsearchItems)

functions[searchItems]: http function initialized (http://localhost:5000/<project-Id>/asia-northeast1/searchItems).

ここでツールチップの通り、Ctrl+クリックで()n内のURLにアクセスしていた。
つまりhttps.onCall のプロトコルの仕様を完全に無視したわけだ。
それは1時間たっても解決するはずないですわ:joy:

POSTでリクエストを送るため、POSTMAN…、ここはVSCodeでお手軽なやつを探して、@toshi0607さんの紹介されているREST Clientを採用。
https://qiita.com/toshi0607/items/c4440d3fbfa72eac840c

request.httpというファイルを作って、下記のリクエストを作成。
<projectId>は自身のものを利用すること。
Content-TypeとBodyの間に1行の空行は必須。

request.http
POST https://asia-northeast1-<projectId>.cloudfunctions.net/searchItems
Content-Type: application/json

{
	"data": {}
}

Send Requestボタンをクリックすると正常に応答が来る。
めでたくBigQueryよりデータが取れている。

response

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
(中略)

{
  "result": [
    {
      "id": 1,
      "text": "item1",
      "timestamp": 1
    },
    {
      "id": 2,
      "text": "item2",
      "timestamp": 2
    }
  ]
}

Conclusion

VSCodeの甘い誘いにより、泥沼に引き込まれた話でした。
functions.https.onCallはクライアントからコールする際は便利ですが、WebAPIとして利用する場合には隠蔽されている部分に準拠する必要があるので注意しましょう。
今回は認証を使っていないため軽傷でしたが、認証が必須のFunctionではAuthorizationにログイン後にuserオブジェクトが持つトークンが必要になります。
トークンは1時間で無効になるおまけ付きなので、REST Clientからコールするよりアプリからコールすることを検討したほうがいいでしょう。
(アプリでは自動的にトークンの期限が延長されるため、期限切れの心配が不要です)

10
5
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
10
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?