Twitter API でツイートする時に Content-Type でハマった
Twitter API のツイートのエンドポイント statuses/update.json
にリクエストした際、以下のエラーが返ってきました。
{“message”:”Could not authenticate you”,”code”:32}
Twitter API でよく見かけるエラーレスポンスのようですが、このメッセージだけでは原因がわからなかったので、解決するまでの道のりをまとめました。
TL;DR
ヘッダーに Content-Type: application/x-www-form-urlencoded
を指定して、ボディは application/x-www-form-urlencoded
の形式でリクエストする。
解決までのフロー
1. axios で以下のリクエストをしたら例のエラーが返ってきた
POST /1.1/statuses/update.json HTTP/1.1
Accept: application/json, text/plain, */*
Content-Type: application/json
Authorization: OAuth ...(省略
User-Agent: axios/0.21.1
Content-Length: 17
Host: api.twitter.com
Connection: close
{"status":"test"}
const url = "https://api.twitter.com/1.1/statuses/update.json";
const data = { status }
const configs = {
headers: {
Authorization: "OAuth ..." // 省略
};
};
return await axios.post(url, data, configs);
node-twitter を使ってみた
2.node-twitter の REST API を使ってツイートできた。
→ コンシューマーキーとアクセストークンはOK
署名の作成のサンプルを確認
3.サンプル通りに署名を作れた。
→ 署名の作り方もOK
リクエストの認証のサンプルを確認
4.サンプル通りにヘッダー文字列を作れた。
→ ヘッダーの作り方もOK
リクエストの認証のサンプルのリクエストを確認
5.リクエストヘッダー に Content-Type: application/x-www-form-urlencoded
がついていたことに気がついた。
POST /1.1/statuses/update.json?include_entities=true HTTP/1.1
Accept: */*
Connection: close
User-Agent:OAuth gem v0.4.4
Content-Type: application/x-www-form-urlencoded
Content-Length:76
Host: api.twitter.com
status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21
6. node-twitter で成功したリクエストヘッダーを確認
Content-Type: application/x-www-form-urlencoded
がついていた。
POST /1.1/statuses/update.json HTTP/1.1
Accept: */*
Connection: close
User-Agent: node-twitter/1.7.1
host: api.twitter.com
content-type: application/x-www-form-urlencoded
content-length: 11
Authorization: OAuth ...(省略
そういえば axios でリクエストするときいつも脳死で application/json
の形式でリクエストしていたような・・・?🤔
7. axios で失敗したリクエストヘッダーを確認
Content-Type: application/json
がついていた。(1.を参照)
やっぱり😩
8. axios の data の形式を変更
JSON形式からクエリパラメーター形式に変更した。
ツイートできた😊
const url = "https://api.twitter.com/1.1/statuses/update.json";
// クエリパラメーター形式
const data = `status=${encodeURIComponent(status)}`;
const configs = {
headers: {
Authorization: "OAuth ..." // 省略
};
};
return await axios.post(url, data, configs);
axios で application/x-www-form-urlencoded を使う
axios の GitHub の Request Config で data に指定できるパラメーターを確認したら、以下のような記述がありました。
// `data` is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', 'DELETE , and 'PATCH'
// When no `transformRequest` is set, must be of one of the following types:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - Browser only: FormData, File, Blob
// - Node only: Stream, Buffer
data: {
firstName: 'Fred'
},
// syntax alternative to send data into the body
// method post
// only the value is sent, not the key
data: 'Country=Brasil&City=Belo Horizonte',
また、Using application/x-www-form-urlencoded format には以下のような記述がありました。
By default, axios serializes JavaScript objects to JSON. To send data in the application/x-www-form-urlencoded format instead, you can use one of the following options.
上記の通り、axios では、data に指定したオブジェクトがデフォルトで JSON 形式にシリアル化されます。
このため、application/x-www-form-urlencoded
の形式で POST するために、Node.js では querystring などを使い、以下のようにする必要があるようです。
const querystring = require('querystring');
axios.post('http://something.com/', querystring.stringify({ foo: 'bar' }));
// 'foo=bar'
おわりに
Twitter API のリファレンスに Content-Type の指定が特になかったので、ほんの少しだけ不親切な気がしました。(初心者にも優しくしてほしいという気持ち)
解決するのに数時間かかりましたが、解決するために1個ずつ紐解いて行く忍耐力は大事だなと感じました。
Discussion