2016-08-29 7 views
1

私は単純なノードベースのAPIを使用しています。これはJSONを解析し、データをPostgresに保存し、適切なレスポンスコード(http 201など) 。 私のコードは次のようになります。Node.js APIからレスポンスコードを正しく送信する方法

router.route('/customer') 

    .post(function(req, res) { 
     Customers = req.body; 
     var numberOfCustomers = Customers.length; 
     for(var i = 0; i < Customers.length; i++){ 
      Customer = Customers[i]; 
      console.log(Customer.Name + " " + Customer.Address); 
      var date = moment(new Date()).unix(); 

      client.query(
       'INSERT into customer (name, address, date_modified) VALUES($1, $2, $3) RETURNING id', 
       [Customer.Name, Customer.Address, date], 
       function(err, result) { 
        if (err) { 
         console.log(err); 
         status = 1; 
        } else { 
         console.log('row inserted with id: ' + result.rows[0].id); 
         if(numberOfCustomers === i) { 
          res.status(201).send({ message: "created" }); 
         } 
        } 
       }); 
     } 
    }) 

私はこのエラーを取得しています:

_

http_outgoing.js:344 
    throw new Error('Can\'t set headers after they are sent.'); 
    ^

Error: Can't set headers after they are sent. 
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:344:11) 

私は私が私のPostgresは、複数の挿入を行うんだという、事実を考慮する必要があります最初の挿入が完了した後で私の応答ヘッダーを送信することはできません。

私の 'POST'ハンドラ内で最も適切な場所は、res.status(201).send({ message: "created" }); です。

+0

最も適切な場所は、リクエストに*単一の返信*を送信する場所です。それはあなたの要求に完全に従っています。 – Amit

答えて

2

建築の決定はさておき、あなたがすることを約束を使用することができます(たとえば、あなたのルートコントローラの内部でそれをやってとは対照的に、応答コードを送信するためのロジックを処理するために、HTTPアダプタとして動作する別のモジュールが必要になる場合があります)すべての挿入が完了するまで待ってから、の単一の応答コードを送信してください。たとえば、次のようなものがあります。

var Promise = require('bluebird'); 
var query = Promise.promisify(client.query); 

router.route('/customer') 
.post(function(req, res) { 
    // all your logic, and then 

    return Promise.all(Customers.map(function() { 
    return query(sql, [Customer.Name, Customer.Address, date]); 
    }) 
    .then(function() { 
    res.status(201).send({ message: 'Created' }); 
    }); 
}); 

この例で使用するAPIについては、the bluebird docsを参照してください。

私はPostgresのAPIに慣れていませんが、概念は似ているはずです。まず、DBへのすべてのリクエストが解決されるまで待つ必要があります。

+0

なぜ約束のネイティブ実装を使用していないのですか? – cbass

+0

@cbassあなたが最新のnode.jsを使用しているなら、あなたは約束のネイティブ実装を使うことができます。または、古いバージョンを使用していて、es6をes5に移行したい場合は、それもオプションです。 –

0

適切なやり方で、Async or lodash libを調べることをおすすめします。

router.route('/customer') 

.post(function(req, res) { 
    var Customers = req.body, 
     numberOfCustomers = Customers.length; 

    for(var i = 0; i < Customers.length; i++){ 
     var Customer = Customers[i]; 
     console.log(Customer.Name + " " + Customer.Address); 
     var date = moment(new Date()).unix(), 
      sql = 'INSERT into customer (name, address, date_modified) VALUES($1, $2, $3) RETURNING id'; 

     client.query(sql, [Customer.Name, Customer.Address, date], 
      function(err, result) { 
       if (err) { 
        console.log(err); 
        res.status(500).json({message: "Server Error", err: err}); 
       } else { 
        console.log('row inserted with id: ' + result.rows[0].id); 
        if (numberOfCustomers === i) { 
         res.status(201).send({ message: "Created" }); 
        } 
       } 
      }); 
    } 
}) 
+0

これは、残念ながら、OPが見ているのと同じ問題を引き起こします。あなたはループ内でヘッダーを変更しています。したがって、最初の反復の後にエラー – Seth

+0

True @Sethが返されますが、 'if'ブロックはエラーがある場合にのみ返され、' Array'内の特定の 'object'に対して' error stack'を返送します。処理できなかったdbをきれいに保つために出口があるべきだと思います。 – akinjide

+0

最初の反復が成功し、2番目の反復が失敗した場合はどうなりますか?ヘッダーは既に設定され、その時点で応答が終了し、既存の問題が再び発生します。 – Seth

2

前述のように、はい、Promisesやasyncなどの非同期ヘルパーはこのような問題に有益です。しかし、私はこの問題を解決するための「最良の」方法は単一のクエリのみを使用することだと考えています。代わりにのみ、クエリごとに挿入を実行する、バッチそうのような単一のクエリに、それらのすべてのアップ:

INSERT into customer (name, address, date_modified) 
VALUES 
    ($1, $2, $3), 
    ($4, $5, $6), 
    ($7, $8, $9), 
    ... 
RETURNING id' 

提案

router.route('/customer').post(function(req, res) { 

    //Fetch customers 
    var customers = req.body; 

    //Store parameters and query inserts for db-query. 
    var params = []; 
    var inserts = []; 

    //For each customer 
    // - Add parameters for query 
    // - Build insert string 
    customers.forEach(function(customer){ 
     inserts.push(
     [ 
      "($", 
      params.push(customer.Name), 
      ", $", 
      params.push(customer.Address), 
      ", ", 
      NOW(), //unnecessary to generate timestamp in js 
      ")", 
     ].join('') 
     ) 
    }); 

    //Build query 
    var query = 'INSERT into customer (name, address, date_modified) VALUES '+ inserts +' RETURNING id'; 

    //Query database in a more simple fashion. 
    client.query(query, params, function(err, result) { 
     if (err) { 
      console.log(err); 
      status = 1; 
     } else { 
      res.status(201).send({ message: "created" }); 
     }); 
    } 
}) 

あなたはES6を使用している場合は、文字列の構築を簡素化することができます文字列テンプレートを使用した操作

customers.forEach(function(customer){ 
    var query = `($${params.push(customer.Name)}, $${params.push(customer.Address)}, NOW())` 
    inserts.push(query); 
}); 

//and 

var query = ` 
    INSERT into customer (name, address, date_modified) 
    VALUES ${inserts} 
    RETURNING id 
`; 
関連する問題