2016-03-30 5 views
1

サービスを呼び出すためのnodejsプロキシがあります。応答では、リクエストはサービスURLにパイプされます(返す前に応答を解析する場合は、正しい方法です)。問題は、JSON.parse(data)でパーサーが失敗することがあるということです。問題のデバッグ中に私が見たことは、解析されているデータが完全ではないということです(たとえサービスが正しく返されたとしても)。 私はパイプやストリームの経験があまりないので、なぜこれが失敗するのか分かりません。パイプレス応答の解析でチャンクされたデータが取得されることがあります

//Request setup 
      r.on('response', function(resp) { 
      if (resp.statusCode === 200) { 
       r.pipe(responseParser(config.get('service:url'))).pipe(res); 
      } else { 
       r.pipe(res); 
      } 
     }); 

//Parser module 
    var _ = require('lodash'), 
     stream = require('stream'); 

    module.exports = function responseParser(url) { 
     var data = '', 
      parser = new stream.Transform({ 
       objectMode: true 
      }); 

     parser._transform = function (chunk, encoding, done) { 
      data += chunk.toString(); 
      done(); 
     }; 

     parser._flush = function (done) { 
      if (data) { 
       var obj = mapValues(JSON.parse(data)); 
        this.push(JSON.stringify(obj)); 
      } 

      done(); 
     }; 

     function mapValues(data){ 
      ... 
     } 

     return parser; 
    } 

私はまだすべてのデータチャンクが返される前にフラッシュが呼び出される理由は時々知らないが、私はそれを避けるためにやったことは、彼らが到着したとことを確認することで、チャンクを解析するだけですチャンクで私はマップする必要がある値の部分データを取得しません。チャンクに目標値の部分的な情報しか含まれていない場合は、それを削除して次のチャンクの先頭に追加します。このようにしてデータが解析されるので、すべてのデータが返されたときにのみflushが呼び出されるという事実に頼る必要はありません。

+0

なぜパーサが閉じられた後に文字列を押すだけの場合は、 'objectMode:true'を使用していますか? – mscdex

+0

残念ながら、それは私が解読しようとしている共有コードなので、私はちょうどそのように見えました。パーサーが閉じられた後に文字列を押すようなことはありません。それが原因だろうか? – Bianca

+0

'this.push(JSON.stringify(obj));'は文字列をプッシュしています。 – mscdex

答えて

0

objectModeは、この場合は不要なので、無効にします。また、あなたは不正な入力の場合のtry-catchでJSONの解析をラップすることをお勧めします:

module.exports = function responseParser(url) { 
    var data = ''; 
    var parser = new stream.Transform(); 
    parser._transform = function(chunk, encoding, done) { 
    data += chunk; 
    done(); 
    }; 
    parser._flush = function(done) { 
    var err; 
    if (data) { 
     try { 
     var obj = mapValues(JSON.parse(data)); 
     this.push(JSON.stringify(obj)); 
     this.push(null); 
     } catch (ex) { 
     err = ex; 
     } 
    } 
    done(err); 
    }; 

    function mapValues(data){ 
    // ... 
    } 

    return parser; 
}; 

ます。またapplication/json最初のようなそれを解析しようとする前にresp.headers['content-type']が含まれていることを確認したいことがあり、あなたがかもしれません毎回新しい_transform()_flush()ファンクションを作成する代わりに、カスタムTransformサブクラスを作成してインスタンス化したいとします。

+0

チャンクされたデータを返すサービスだと思いますか?同じ呼び出しがJSONオブジェクト全体を返すことがあるため、パイプ+ストリームで何かがうまくいかないと思われます...また、バックエンドサービスへのプロキシ呼び出しから、この問題はいずれか一方にのみ表示されます。 – Bianca

0

これを自分で書くのではなく、ストリームの解析方法を知っているストリーミングJSONパーサーを使用してみませんか?例えばJSONStreamである。

もう1つの選択肢は、stream-to-promiseを使用して、読み込みストリームをPromiseに変換するだけで、JSONのバッファに解決され、解析することができます。

さらに、プロキシがJSONを解析する理由は何ですか?

+0

プロキシはJSONを解析しています。その結果、レスポンスからいくつかのURLを隠す必要があるからです。これらのURLはおそらくサービスに渡されている可能性がありますが、私はそのビットを変更する権限はありません。私はJSONStreamオプションとstream-to-promiseオプションを調べます。たぶんそれは少しコードをきれいにするのに役立ちます。 – Bianca

+0

こんにちは、私は最終的にJSONStreamを詳しく見る機会を得ました。それはJSONコンテンツのフィルタリングに使用されているようですが、私の応答では特定のキーの値だけを置き換える必要があります。ストリーム・ツー・プロミスはもっと面白く見えましたが、私はパースされた結果をレスポンスにパイプする方法を理解していませんでした。 – Bianca

関連する問題