MovableTypeのDataAPIを叩こうとしたら以前はXMLRPCだったのでRest対応はどうするか困ってしまった件をなんとかする。
経緯
昔、MovableTypeのデータAPIを叩いて大量のデータを投稿するとかよくやってたんだけど、そのころはXMLRPCを使ってた。
イマドキは、Rest APIでしょ!ってんで、MovableTypeのデータAPI V6ではRestが主流っぽかった。別にXMLRPCも使えるんだろうけど。
ということで、PHPのGuzzlehttpを使ってたんだけど、認証系とデータの取得系は行けるけど、データの更新系が全然うまくいかずに困ってしまってワンワンワワーン。
JavaScriptのDataAPIのサンプルがあるよ。
DataAPI使ったサンプルないかなーって公式のページを見つけたんだけど、読んでもよーわからん…というかこれNodeでやんの?えー。とか思ってたらいいページがあった。これ10年前かよ!
で作ってみた
こんな感じ。JavaScriptSDKはmt-staticフォルダにあるので、それを使う。今回の mt-staticのパスは ↓のコードを参照。
https://mt.example.com/test_data_api.html を作って、https://mt.example.com/mt.cgi のMovableTypeのデータAPIを叩くイメージ。MovableTypeの管理画面からAPIの許可を出しておいてくれ。
あと、Entryを投稿するには認証が必要なので、認証通ってない場合は「Login」のリンクをクリックしてID/Webサービスパスワード(管理画面へのログインPASSWORDではない!同じログイン画面を使うけどね)でログインする。
<html> <head> <meta charset="UTF-8"/> <title>MT Data API</title> <script src="/mt-static/data-api/v6/js/mt-data-api.min.js"></script> </head> <body> <div id="entries"></div> <p><a href="#" id="login">Login</a></p> <p>UserName:<span id="username"></span></p> <button id="createEntry">Create Entry</button> <script> const siteId = 1; var api = new MT.DataAPI({ baseUrl: "/mt-data-api.cgi", clientId: "your-client-id" }); function goToAuthorizationUrl() { // 認証が成功した場合、現在の URL に戻ってくる const href = location.href; document.getElementById('login').href = api.getAuthorizationUrl(href); } if (! api.getTokenData()) { // トークンのデータを持っていない goToAuthorizationUrl(); } api.listEntries(siteId, function(response) { let html = ''; if (response.error) { html += "<p>データ取得エラーです。</p>"; return; } html += "<ul>"; for (var i = 0; i < response.items.length; i++) { var entry = response.items[i]; html += "<li>" + entry.title + "</li>"; } html += "</ul>"; var entries = document.getElementById("entries"); entries.innerHTML = html; }); api.getUser('me', function(response) { if (response.error) { if (response.error.code === 401) { // 以前取得したトークンが期限切れになっているなどの理由で再認証が必要 goToAuthorizationUrl(); return; } // エラー処理 return; } var user = response; document.getElementById('login').style.display = 'none'; document.getElementById('username').textContent = user.displayName; }); const entryData = { title : "entry from api", body: 'body sample' }; document.getElementById('createEntry').addEventListener('click', ()=> { api.createEntry(siteId, entryData, function(response) { console.log(response); }); }); </script> </body> </html>
POSTデータについて
で、上記のスクリプトを動かすと、POSTデータが以下のようになっている。(HTTPリクエストのボディ部分)
------WebKitFormBoundaryfgVhBpmmR6viaXNh
Content-Disposition: form-data; name="entry"
{"title":"entry from api","body":"body sample"}
------WebKitFormBoundaryfgVhBpmmR6viaXNh--
Content-Type: multipart/form-data; で送るのは知ってたけど、Content-Disposition: form-data; name="entry"これはなんぞ?そういえば、MT側でPOSTデータをチェックしている時にEntryってデータがないよってエラーが出てたんだよな…ここで指定するのか…
今までのPOSTデータ
今までGuzzlehttpで
<?php $response = $client->post($url, [ 'headers' => [ 'X-MT-Authorization' => 'MTAuth accessToken=' . $access_token, ], 'json' => [ 'entry' => [ 'title' => 'sample entry title', 'body' => 'sample entry BODY', 'status' => 'Publish', 'date' => '2025-01-17T12:00:00Z', ], 'publish' => 1, ], ]);
とかやってたけど、こんなJSONが返ってきた。
{"error":{"code":400,"message":"A resource \"entry\" is required."}}
Content-Disposition: form-data; name="entry"が含まれてないからMT側で「Entryデータが無い」って言われてたのか…
正しいPOSTデータ
こんな感じ。Body部分はmultipartで指定するのだった。
<?php $response = $client->post($url, [ 'headers' => [ 'X-MT-Authorization' => 'MTAuth accessToken=' . $access_token, ], 'multipart' => [ [ 'name' => 'entry', 'contents' => '{"title":"entry from api(PHP)","body":"body sample"}', ], ], ]);