このHTTPリクエストがAWSLambdaで機能しないのはなぜですか?


89

AWS Lambdaを使い始めて、ハンドラー関数から外部サービスをリクエストしようとしています。この回答によると、HTTPリクエストは問題なく機能するはずですが、そうでないことを示すドキュメントは見つかりませんでした。(実際、人々はTwilio APIを使用してSMSを送信するコードを投稿しています。)

私のハンドラーコードは次のとおりです。

var http = require('http');

exports.handler = function(event, context) {
  console.log('start request to ' + event.url)
  http.get(event.url, function(res) {
    console.log("Got response: " + res.statusCode);
  }).on('error', function(e) {
    console.log("Got error: " + e.message);
  });

  console.log('end request to ' + event.url)
  context.done(null);
}

CloudWatchログに次の4行が表示されます。

2015-02-11 07:38:06 UTC START RequestId: eb19c89d-b1c0-11e4-bceb-d310b88d37e2
2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 start request to http://www.google.com
2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 end request to http://www.google.com
2015-02-11 07:38:06 UTC END RequestId: eb19c89d-b1c0-11e4-bceb-d310b88d37e2

私はそこに別の行を期待します:

2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 Got response: 302

しかし、それは欠けています。ローカルマシンのノードでハンドラーラッパーなしで重要な部分を使用している場合、コードは期待どおりに機能します。

inputfile.txt私が使用しているがためであるinvoke-asyncコールは以下です:

{
   "url":"http://www.google.com"
}

リクエストを行うハンドラコードの部分が完全にスキップされているようです。私はリクエストlibから始めてhttp、最小限の例を作成するためにプレーンを使用することに戻りました。また、ログを確認するために制御しているサービスのURLをリクエストしようとしましたが、リクエストが届きません。

私は完全に困惑しています。NodeやAWSLambdaがHTTPリクエストを実行しない理由はありますか?


これは、HTTPリクエストにユーザーエージェントがないことが原因である可能性があると思います。
Ma'moon Al-Akash 2015

4
これを書いている時点では、これは現在AWSフォーラムのLambdaフォーラムのトップの質問です。それは私を狂わせ、他の多くの人々も動かしています。
ノストラダムス2015年

@Nostradamus追加のフィードバック、修正、賛成に感謝します。ここに送ってください
;

1
Twilloの例から、Alexaノードのサンプルバンドルに付属しているいくつかのデフォルトの例、そしてcontext.done()メソッドまですべてを試しました。httpPOSTが機能していません。POSTリクエストコードの完全なサンプルを投稿することは可能ですか?
chheplo 2015

回答:


79

もちろん、私はその問題を誤解していた。AWS自身が述べているように

Lambdaで初めてnodejsに遭遇した場合、一般的なエラーは、コールバックが非同期で実行されることを忘れてcontext.done()、別のコールバック(S3.PUT操作など)が完了するのを本当に待つつもりだったときに元のハンドラーを呼び出し て、関数を強制することです。作業が不完全な状態で終了します。

context.doneリクエストのコールバックが発生する前に呼び出しを行っていたため、関数が事前に終了していました。

動作するコードは次のとおりです。

var http = require('http');

exports.handler = function(event, context) {
  console.log('start request to ' + event.url)
  http.get(event.url, function(res) {
    console.log("Got response: " + res.statusCode);
    context.succeed();
  }).on('error', function(e) {
    console.log("Got error: " + e.message);
    context.done(null, 'FAILURE');
  });

  console.log('end request to ' + event.url);
}

更新: 2017年以降、AWSは古いNodejs 0.10を非推奨にし、新しい4.3ランタイムのみが利用可能になりました(古い関数を更新する必要があります)。このランタイムでは、ハンドラー関数にいくつかの変更が加えられました。新しいハンドラーには3つのパラメーターがあります。

function(event, context, callback)

あなたはまだわかりますがsucceeddoneおよびfailコンテキストパラメータに、AWSは、使用することをお勧めcallback代わりに機能するか、nullデフォルトで返されます。

callback(new Error('failure')) // to return error
callback(null, 'success msg') // to return ok

完全なドキュメントはhttp://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.htmlにあります。


4
では、ハンドラーコードをどのように機能させるのですか?私の理解では、コールバック関数が呼び出されるようにcontext.done()を削除する必要があります。しかし、あなたのコードはまだ私には機能しません。:(
mabeiyi 2015年

3
context.done()コールは(成功およびエラーの場合の)コールバックに移動する必要があります。
awendt 2015年

2
まだ問題はありませんでしたが、ラムダで前進するときに覚えておくとよいでしょう。
デビッド

LambdaからローカルシステムでAPIを呼び出す方法に関するアイデアはありますか?
Amit Kumar Ghosh

2
2017年の更新で2015年の質問を更新するための小道具!
エース

19

ノードを使用したHttpリクエストの簡単な作業例。

const http = require('https')
exports.handler = async (event) => {
    return httprequest().then((data) => {
        const response = {
            statusCode: 200,
            body: JSON.stringify(data),
        };
    return response;
    });
};
function httprequest() {
     return new Promise((resolve, reject) => {
        const options = {
            host: 'jsonplaceholder.typicode.com',
            path: '/todos',
            port: 443,
            method: 'GET'
        };
        const req = http.request(options, (res) => {
          if (res.statusCode < 200 || res.statusCode >= 300) {
                return reject(new Error('statusCode=' + res.statusCode));
            }
            var body = [];
            res.on('data', function(chunk) {
                body.push(chunk);
            });
            res.on('end', function() {
                try {
                    body = JSON.parse(Buffer.concat(body).toString());
                } catch(e) {
                    reject(e);
                }
                resolve(body);
            });
        });
        req.on('error', (e) => {
          reject(e.message);
        });
        // send the request
       req.end();
    });
}

これありがとう。これは、Lambdaがawait構文を使用しているため、2019年にこのページに表示される最良の回答です。
TaneemTee19年

3
node-fetch requestデフォルトではライブラリなどはLambdaで利用できないため、これを見つけてベストアンサーを見つけるのに1時間以上かかりました。
アレックス・C

そこにあるサンプルコードの多くは、現在壊れているようです。これは、2020年3月の時点で、Node.js12.xでAWSLambdaを使用したサンプルコードです
Muhammad Yussuf

ラムダ関数内のデータを使用してPOSTリクエストを行う方法を誰かが説明できますか?
パビンドゥ

11

ええ、awendtの答えは完璧です。作業コードを表示するだけです... context.succeed( 'Blah');がありました。reqPost.end();の直後の行 ライン。以下に示す場所に移動すると、すべてが解決しました。

console.log('GW1');

var https = require('https');

exports.handler = function(event, context) {

    var body='';
    var jsonObject = JSON.stringify(event);

    // the post options
    var optionspost = {
        host: 'the_host',
        path: '/the_path',
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        }
    };

    var reqPost = https.request(optionspost, function(res) {
        console.log("statusCode: ", res.statusCode);
        res.on('data', function (chunk) {
            body += chunk;
        });
        context.succeed('Blah');
    });

    reqPost.write(jsonObject);
    reqPost.end();
};

4

Node10.Xバージョンでこの問題に直面しました。以下は私の作業コードです。

const https = require('https');

exports.handler = (event,context,callback) => {
    let body='';
    let jsonObject = JSON.stringify(event);

    // the post options
    var optionspost = {
      host: 'example.com', 
      path: '/api/mypath',
      method: 'POST',
      headers: {
      'Content-Type': 'application/json',
      'Authorization': 'blah blah',
    }
    };

    let reqPost =  https.request(optionspost, function(res) {
        console.log("statusCode: ", res.statusCode);
        res.on('data', function (chunk) {
            body += chunk;
        });
        res.on('end', function () {
           console.log("Result", body.toString());
           context.succeed("Sucess")
        });
        res.on('error', function () {
          console.log("Result Error", body.toString());
          context.done(null, 'FAILURE');
        });
    });
    reqPost.write(jsonObject);
    reqPost.end();
};

3

私はまったく同じ問題を抱えていましたが、NodeJSでのプログラミングは、JavaScriptに基づいているためPythonやJavaとは実際には異なることに気付きました。興味を持ったり、この質問に来るかもしれない新しい人々が何人かいるかもしれないので、私は単純な概念を使用しようとします。

次のコードを見てみましょう:

var http = require('http'); // (1)
exports.handler = function(event, context) {
  console.log('start request to ' + event.url)
  http.get(event.url,  // (2)
  function(res) {  //(3)
    console.log("Got response: " + res.statusCode);
    context.succeed();
  }).on('error', function(e) {
    console.log("Got error: " + e.message);
    context.done(null, 'FAILURE');
  });

  console.log('end request to ' + event.url); //(4)
}

httpパッケージ(1)のメソッドを呼び出すと、イベントとして作成され、このイベントは別のイベントを取得します。'get'関数(2)は、実際にはこの個別のイベントの開始点です。

これで、(3)の関数は別のイベントで実行され、コードはパスの実行を継続し、(4)に直接ジャンプして終了します。これ以上、何もする必要がないためです。

しかし、(2)で発生したイベントはまだどこかで実行されており、終了するまでには時間がかかります。かなり奇妙ですよね?いいえ、そうではありません。これがNodeJSの仕組みであり、この概念に頭を悩ませることが非常に重要です。これは、JavaScriptPromisesが役立つ場所です。

JavaScript Promisesについて詳しくは、こちらをご覧ください。。一言で言えば、コードの実行をインラインに保ち、新しい/余分なスレッドを生成しないようにするには、JavaScriptPromiseが必要になります。

一般的なNodeJSパッケージのほとんどでは、APIのPromisedバージョンが利用可能ですが、BlueBirdJSのような他のアプローチでも同様の問題に対処できます。

上で書いたコードは、次のように大まかに書き直すことができます。

'use strict';
console.log('Loading function');
var rp = require('request-promise');
exports.handler = (event, context, callback) => {    

    var options = {
    uri: 'https://httpbin.org/ip',
    method: 'POST',
    body: {

    },
    json: true 
};


    rp(options).then(function (parsedBody) {
            console.log(parsedBody);
        })
        .catch(function (err) {
            // POST failed... 
            console.log(err);
        });

    context.done(null);
};

AWS Lambdaにインポートする場合、上記のコードは直接機能しないことに注意してください。Lambdaの場合、モジュールもコードベースでパッケージ化する必要があります。


はい、約束します!context.done()呼び出しを連鎖finallyメソッドに移動することを検討しますが。
crftr 2016年

3

リクエストを実行するさまざまな方法について、ウェブ全体で多くの投稿を見つけましたが、AWSLambdaで応答を同期的に処理する方法を実際に示しているものはありません。

これは、httpsリクエストを使用し、レスポンスの本文全体を収集して返しprocessBody、結果とともにリストにない関数に制御を渡すノード6.10.3ラムダ関数です。このコードではhttpとhttpsは交換可能だと思います。

私は非同期ユーティリティモジュールを使用しています。これは初心者にとって理解しやすいものです。それを使用するには、それをAWS Stackにプッシュする必要があります(サーバーレスフレームワークをお勧めします)。

データはチャンクで返され、グローバル変数に収集され、最後にデータがend編集されたときにコールバックが呼び出されることに注意してください。

'use strict';

const async = require('async');
const https = require('https');

module.exports.handler = function (event, context, callback) {

    let body = "";
    let countChunks = 0;

    async.waterfall([
        requestDataFromFeed,
        // processBody,
    ], (err, result) => {
        if (err) {
            console.log(err);
            callback(err);
        }
        else {
            const message = "Success";
            console.log(result.body);
            callback(null, message);
        }
    });

    function requestDataFromFeed(callback) {
        const url = 'https://put-your-feed-here.com';
        console.log(`Sending GET request to ${url}`);
        https.get(url, (response) => {
            console.log('statusCode:', response.statusCode);
            response.on('data', (chunk) => {
                countChunks++;
                body += chunk;
            });
            response.on('end', () => {
                const result = {
                    countChunks: countChunks,
                    body: body
                };
                callback(null, result);
            });
        }).on('error', (err) => {
            console.log(err);
            callback(err);
        });
    }
};

0

ここに画像の説明を入力してください

上記のコードをAPIゲートウェイのGET-IntegrationRequest> mappingセクションに追加します。


-14

はい、実際、AWSLambdaのようなHTTPエンドポイントにアクセスできる理由はたくさんあります。

AWSLambdaのアーキテクチャ

これはマイクロサービスです。Amazon Linux AMI(バージョン3.14.26–24.46.amzn1.x86_64)を使用してEC2内で実行し、Node.jsを使用して実行します。メモリは128mbと1gbの間である可能性があります。データソースがイベントをトリガーすると、詳細がパラメーターとしてLambda関数に渡されます。

何が起きましたか?

AWS Lambdaはコンテナ内で実行され、コードはパッケージまたはモジュールとともにこのコンテナに直接アップロードされます。たとえば、ラムダ関数を実行しているLinuxマシンに対してSSHを実行することはできません。監視できるのはログだけですが、CloudWatchLogsとランタイムからの例外があります。

AWSがコンテナの起動と終了を処理し、コードを実行するだけです。したがって、require( 'http')を使用しても、このコードが実行される場所がこのために作成されていないため、機能しません。


5
あなたは私の問題を誤解しているかもしれません。コンテナで実行されているLambdaコードについて知っていますが、基盤となるマシンにアクセスできないことを知っています。私は侵入しようとはしていません。コードは外部エンドポイントにアクセスしようとしていますが、Lambdaはそれを非常にうまく行うことができます。私自身の答えで指摘したように、問題はまったく別のものでした。
awendt 2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.