LoginSignup
7
7

More than 3 years have passed since last update.

Azure Functions 内で追加モジュールを使わずに HTTP Client を動かす方法

Posted at

何に使えるのか?

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 に伝達している
  • レスポンスボディに対する処理を 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

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