33

Node.jsとExpressにアプリがあります。テストを書く必要があります。Expressアプリのエラーの処理に問題があります。私はこれを見つけましたEADDRINUSEのようなnode.js/expressサーバーエラーをキャッチするにはどうすればよいですか?、しかしそれは私にはうまくいきません、私は理由がわかりません。expressApp.listen()の実行中に発生する可能性のあるエラー(EADDRINUSE、EACCESなど)を処理したい。

express = require('express')
listener = express()

#doesn't work for me
listener.on('uncaughtException', (err) ->
  #do something
)

#doesn't work too
listener.on("error", (err) ->
  #do something
)

#this works, but it caughts all errors in process, I want only in listener
process.on('uncaughtException', (err) ->
  #do something
)

listener.listen(80) #for example 80 to get error

何か案は?

4

3 に答える 3

103

これでうまくいくはずです:

listener.listen(80).on('error', function(err) { });

実際に行うことlistener.listenは、HTTPサーバーを作成し、それに対してlistenを呼び出すことです。

app.listen = function(){
  var server = http.createServer(this);
  return server.listen.apply(server, arguments);
};
于 2014-11-20T13:08:58.560 に答える
29

まず、expressJSはuncaughtExceptionイベントをスローしませんが、プロセスはスローします。そのため、コードが機能しないのは当然です。

したがって、process.on('uncaughtException',handler)代わりに:を使用してください。

次に、expressJSは、次のように、この目的のために提供するミドルウェア関数を使用するという、エラー処理の標準的な手段をすでに提供しています。

app.configure(function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});

この関数は、オプションのスタックトレースを使用してエラーメッセージをクライアントに返します。これは、connectJSerrorHandlerに記載されています

(errorHandlerは実際にはconnectJSの一部であり、expressJSによってのみ公開されることに注意してください。)

既存のerrorHandlerが提供する動作がニーズに十分でない場合、そのソースはconnectJSのerrorHandlerミドルウェアにあり、ニーズに合わせて簡単に変更できます。

もちろん、この関数を直接変更するのではなく、これを行う「正しい」方法は、次のように、connectJSバージョンを開始点として使用して独自のerrorHandlerを作成することです。

var myErrorHandler = function(err, req, res, next){
    ...
    // note, using the typical middleware pattern, we'd call next() here, but 
    // since this handler is a "provider", i.e. it terminates the request, we 
    // do not.
};

そしてそれをexpressJSに次のようにインストールします:

app.configure(function(){
    app.use(myErrorHandler);
});

よく書かれたチュートリアルについては、connectJSのミドルウェアの考え方と、Connect / Express用のミドルウェアの書き方については、「JustConnectit filter参照してください。provider

また、これらが役立つ場合があります。

最後に、expressJSのテストに関する優れた情報源は、独自のテストで見つけることができます。

于 2012-11-10T22:41:41.750 に答える
11

言及:Marius Tibeicaの回答は完全で素晴らしいものであり、david_pのコメントもあります。Rob Raischの答えもそうです(探索するのは興味深いです)。 https://stackoverflow.com/a/27040451/7668448
https://stackoverflow.com/a/13326769/7668448

知らせ

この最初の方法は悪い方法です!参考にしておきます!更新セクションを参照してください!良いバージョンのために!また、その理由の説明も!

 悪いバージョン

これが便利だと思う人のために、ここではビジーポート処理を実装する関数を使用します(ポートがビジーの場合、ビジーポートがなくなるまで次のポートで試行します)

app.portNumber = 4000;
function listen(port) {
    app.portNumber = port;
    app.listen(port, () => {
        console.log("server is running on port :" + app.portNumber);
    }).on('error', function (err) {
        if(err.errno === 'EADDRINUSE') {
            console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
            listen(port + 1)
        } else {
            console.log(err);
        }
    });
}

listen(app.portNumber);

関数listenは、それ自体を再帰的に呼び出しています。ポートビジーエラーの場合。毎回ポート番号をインクリメントします。

更新完全にやり直し

 コールバックフルバージョン

まず第一に、このバージョンはnodejshttp.Server.listen()メソッドと同じシグネチャに従うバージョンです!

function listen(server) {
    const args = Array.from(arguments);
    // __________________________________ overriding the callback method (closure to pass port)
    const lastArgIndex = arguments.length - 1;
    let port = args[1];
    if (typeof args[lastArgIndex] === 'function') {
        const callback = args[lastArgIndex];

        args[lastArgIndex] = function () {
            callback(port);
        }
    }

    const serverInstance = server.listen.apply(server, args.slice(1))
        .on('error', function (err) {
            if(err.errno === 'EADDRINUSE') {
                console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
                port += 1;
                serverInstance.listen.apply(serverInstance, [port].concat(args.slice(2, lastArgIndex)));
            } else {
                console.log(err);
            }
        });

    return serverInstance;
}

サイン:

listen(serverOrExpressApp、[port [、host [、backlog]]] [、callback])

と同じように

https://nodejs.org/api/net.html#net_server_listen_port_host_backlog_callback

コールバック署名がに変更されます

(ポート)=>無効

 利用方法:

const server = listen(app, 3000, (port) => {
    console.log("server is running on port :" + port);
});

// _____________ another example port and host
const server = listen(app, 3000, 'localhost', (port) => {
    console.log("server is running on port :" + port);
});

 説明

古い例とは対照的に!このメソッドはそれ自体を呼び出しません!

重要な要素:

  • app.listen()の最初の呼び出しは、net.Serverインスタンスを返します
  • イベントを一度バインドした後、同じnet.Serverインスタンスにもう一度listenを呼び出すと、再接続が試行されます。
  • エラーイベントリスナーは常にそこにあります!
  • エラーが発生するたびに、再試行します。
  • ポート変数は、コールバックへのクロージャで再生されます!コールバックが呼び出されると、正しい値が渡されます。

重要なのは

serverInstance.listen.apply(serverInstance, [port].concat(args.slice(2, lastArgIndex)));

ここでコールバックをスキップするのはなぜですか!?

コールバックが追加されたら!これは、サーバーインスタンスの内部で配列に保持されます。別のものを追加すると!複数のトリガーがあります!(試行+1)の数について。したがって、最初の試行にのみ含めます。

そうすれば、サーバーインスタンスを直接返すことができます。そしてそれを使って試み続けてください!そして、それはきれいに行われます!

シンプルバージョンポートのみ

それも一瞥でよりよく理解するのに役立ちます

function listen(server, port, callback) {
    const serverInstance = server.listen(port, () => { callback(port) })
        .on('error', function (err) {
            if(err.errno === 'EADDRINUSE') {
                console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
                port += 1;
                serverInstance.listen(port);
            } else {
                console.log(err);
            }
        });

    return serverInstance;
}

ここで、パラメーターポート変数はクロージャーで再生されます!

ES6フルバージョン

function listen(server, ...args) {
    // __________________________________ overriding the callback method (closure to pass port)
    const lastArgIndex = args.length - 1;
    let port = args[0];
    if (typeof args[lastArgIndex] === 'function') {
        const callback = args[lastArgIndex];

        args[lastArgIndex] = function () {
            callback(port);
        }
    }

    const serverInstance = server.listen(server, ...args)
        .on('error', function (err) {
            if(err.errno === 'EADDRINUSE') {
                console.log(`----- Port ${port} is busy, trying with port ${port + 1} -----`);
                port += 1;
                serverInstance.listen(...[port, ...args.slice(1, lastArgIndex)])
            } else {
                console.log(err);
            }
        });

    return serverInstance;
}

古いバージョンが悪い理由

正しく言うと、そうではありません!しかし、最初のバージョンでは!失敗するたびに関数自体を呼び出します。そして、それが新しいインスタンスを作成するたびに!ガベージコレクターはいくつかの筋肉を動かします!

この関数は開始時に1回だけ実行されるため、問題ありません。

古いバージョンはサーバーインスタンスを返しませんでした!

追加(@ sakib11の場合)

@ sakib11のコメントを見て、彼がどのような問題に陥っているのかを確認できます。思いやりがあります!

また、コメントの中で、promiseバージョンとクロージャーゲッターパターンについて言及しました!私はそれらを面白いとは思わない!上記の方法は、nodejsと同じ署名を尊重するだけです!そして、コールバックもうまくいきます!そして、サーバー参照を書き留めています!約束バージョン付き!約束が返され、解決策ですべての要素を渡します!serverInstance +ポート!

そして、クロージャーゲッターパターンが気になるなら!(ここは悪いです)

このメソッド内で、サーバーインスタンスを参照する参照を作成します。実行中にサーバーインスタンスを返すことができなかった場合(イメージングは​​不可能でした!したがって、新しいインスタンスが作成されるたびに!パターンはクロージャー(そのスコープのメソッド)を作成して返します!

だから使用のために

const getServer = listen(port, () => {
   console.log('Server running at port ' + getServer().address().port);
   const io = socketIo(getServer(), {});
}); 

しかし、それはただのオーバーヘッドであり、特にサーバーが完了するのを待つ必要があります!コールバックを使用するように設定しない限り!または約束を返します!

そして、それは複雑すぎます!そして、まったく良くありません!

それは私がそれを言ったからです!

そして、上記の方法は微調整することができます!試行回数制限を追加するには!そして、いくつかのイベントやフックを追加してください!しかしよく!通常、必要なのは、試行して作成するだけの単純な関数だけです。私にとっては、上記で十分です。

良いリンク

ドキュメントから

app.listen()メソッドはhttp.Serverオブジェクトを返し、(HTTPの場合)次の便利なメソッドです。

app.listen = function () {
  var server = http.createServer(this)
  return server.listen.apply(server, arguments)
}
于 2018-11-01T23:29:23.123 に答える