何に使えるのか?
Azure Functions から SaaS 等の REST API が呼べるようになります。
論よりコード
※ サーバ側には WebHook.site を利用しています
const http = require('https');
module.exports = function (context, req) {
const url = `https://webhook.site/9e3f8370-c5e8-4771-89c3-71846257a7c7`;
const postData = {"k1": 1, "k2": "v2"};
const postBody = JSON.stringify(postData);
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postBody)
}
};
const client = http.request(url, options, (res) => {
context.log(res.statusCode);
context.log(res.headers);
let resBody = '';
res.on('data', (chunk) => { resBody += chunk; });
res.on('end', () => {
context.log(resBody);
context.res = {status: 204};
context.done();
});
});
client.on('error', (e) => {
context.log.error(e);
context.res = {status: 500};
context.done();
});
client.write(postBody);
client.end();
};
実行結果
2019-10-18T05:10:36.631 [Information] Executing 'Functions.HttpTrigger1' (Reason='This function was programmatically called via the host APIs.', Id=c5825099-7df7-4315-999a-fd863b3b157a)
2019-10-18T05:10:38.005 [Information] 201
2019-10-18T05:10:38.006 [Information] { server: 'nginx/1.10.3',
'content-type': 'text/plain; charset=UTF-8',
'transfer-encoding': 'chunked',
connection: 'close',
'x-request-id': '7a4ed117-26a0-4051-880e-a4378fb00a19',
'x-token-id': '9e3f8370-c5e8-4771-89c3-71846257a7c7',
'cache-control': 'no-cache, private',
date: 'Fri, 18 Oct 2019 05:10:37 GMT',
'x-ratelimit-limit': '100',
'x-ratelimit-remaining': '99' }
2019-10-18T05:10:38.006 [Information] GET-Wild
2019-10-18T05:10:38.007 [Information] Executed 'Functions.HttpTrigger1' (Succeeded, Id=c5825099-7df7-4315-999a-fd863b3b157a)
実装のポイント
-
exports
へは、同期関数として宣言している (async
が無い)- 理由は後述
-
end
イベントハンドラ内、およびerror
イベントハンドラ内でcontext.done()
を呼び出して関数の終了を Azure Functions に伝達している- 同期関数として宣言した場合は
context.done()
を呼び出す必要がある
- 同期関数として宣言した場合は
- レスポンスボディに対する処理を
end
イベントハンドラに移している- 正直これはどうでもいい
async 関数内では request メソッドの callback が実行されない
なぜこんなエントリーを書いたのかというと、Azure Functions における Node.js の最近のランタイムでは async/await を用いた非同期処理が推奨されており、 util.promisify を用いた async/await 化の方法も掲載されているのですが、これが適用可能なメソッドの条件が様々ある中、残念なことに (私は) HTTP/HTTPS モジュールの request メソッドを async/await 対応させることができませんでした。
もちろん Promise を使えばいいのですが、はっきり言ってデカイ。ちょこっとやりたい事を実現するにはデカすぎる。 (だから util.promisify が産まれたのだから)
ということで、最初のコードを見ていただくと module.exports = function (...
と async
が無くなっていることにお気づきになるでしょう。
もし、Node.js の HTTP モジュールのページにあるサンプル実装 を、そのまま Azure Functions で async 宣言内への実装をすると以下のようなコードになると思いますが、結論から言えば POST は送信されるけど、そのレスポンスを処理する callback は動きません。
const http = require('https');
module.exports = async function (context, req) {
const url = `https://webhook.site/9e3f8370-c5e8-4771-89c3-71846257a7c7`;
const postData = {"k1": 1, "k2": "v2"};
const postBody = JSON.stringify(postData);
const options = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(postBody)
}
};
const client = http.request(url, options, (res) => {
context.log(res.statusCode);
context.log(res.headers);
res.on('data', (chunk) => {
context.log(chunk);
context.res = {status: 204};
});
});
client.on('error', (e) => {
context.log.error(e);
context.res = {status: 500};
});
client.write(postBody);
client.end();
};
実行結果
2019-10-18T05:09:22.257 [Information] Executing 'Functions.HttpTrigger1' (Reason='This function was programmatically called via the host APIs.', Id=539cfd67-bcd9-4af6-90fd-cf6405fbe085)
2019-10-18T05:09:23.243 [Information] Executed 'Functions.HttpTrigger1' (Succeeded, Id=539cfd67-bcd9-4af6-90fd-cf6405fbe085)
POST メソッド以外は?
options.method
に 'GET'
とか入れれば動くよ。
https なのに http とか書いてるのは、何?
const http = require('https');
// でも
const http = require('http');
// でも、後ろのコードに影響がないようにしただけ。
あとがき
前に request npm パッケージを入れて HTTP リクエストする方法 を書いたけど、なんだか動かなくなっちゃったから調べたの。標準モジュールだけで動くのはありがたいけれど、async/await がわからんね。
EoT