Search code examples
dartdart-async

Dartlang server call my function more than once


I'm sending a data to the server like this:

 save(){ 
   var el = this.parent.nodes;
   print(el);print(el.length);
   request = new HttpRequest();
    if(el.length == 1) print('No lines to save!');
     else           
     {        
      var opt = el[i].shadowRoot.nodes[0].options[el[i].shadowRoot.nodes[0].selectedIndex].text;
      print(this.parent.nodes.length);
      for(var i=1; i < el.length; i++)
             {                         
                        orderLines.add({ 
                             'poid': orderHeader[0]['OrderID'],
                            'ponum': orderHeader[0]['onum'],
                            'lnum' : i.toString(),
                           'itmid' :el[i].shadowRoot.nodes[0].value,
                           'icode' : opt,
                              'qty': el[i].shadowRoot.nodes[1].value,
                             'rqty': 0,
                             'bqty': el[i].shadowRoot.nodes[1].value,
                             'iqty': 0,
                            'biqty': el[i].shadowRoot.nodes[1].value,
                            'price': el[i].shadowRoot.nodes[2].value,
                              'rdd': orderHeader[0]['rdd'],
                              'eta': '',
                             'flag': 0 
                                        });  

                        print(orderLines);

                        request.onReadyStateChange.listen(onData_save);   
                        request.open('POST', host+'/sPO');
                        request.send(JSON.encode(orderLines));  
               }
      }
  }

and my server side function is:

void main() {
  connections = new List<WebSocket>();

  HttpServer.bind(HOST, PORT).then((HttpServer server) {
   print('Server listening on port ${PORT}.');
   server.listen((HttpRequest request) {
    if (WebSocketTransformer.isUpgradeRequest(request)) {
      WebSocketTransformer.upgrade(request).then(handleWS); 
    } else gotMSG(request);        
    });
  }); 
}

handleWS(WebSocket ws){

connections.add(ws);
print('Client connected, there are now ${connections.length} client(s) connected.');
ws.listen((String message) {
  for (WebSocket connection in connections) {
  connection.add(message);
  }
},
onDone: () {
 connections.remove(ws);
 print('Client disconnected, there are now ${connections.length} client(s) connected.');
});
}

 void gotMSG(HttpRequest request) {
 switch (request.method) {
  case 'POST': 
    handlePost(request);
    break;
  case 'OPTIONS':
    handleOptions(request);
    break;
  default:
    defaultHandler(request);
 }
}

void serveRequest(HttpRequest request){
  print('Listening for GET and POST on http://$HOST:$PORT');
  request.response.statusCode = HttpStatus.FORBIDDEN;
   request.response.reasonPhrase = "WebSocket connections only";
   request.response.close();  
}

void handlePost(HttpRequest req) {
  HttpResponse res = req.response;
     switch (req.uri.path) {

       case '/login': login(req); break;
       ...
       case '/sPO': savePO(req); break;

       default: break;
     }     
}

The /sPO => savePO is executed once if the order sent is of one line only, but if n lines in the order, the function is executed more than once, could not find a pattern for that, In the SavePO I used oracledart pub, so thought something wrong in it, and tried postgresql pub, but got same results, the savePO function is:

void savePO(HttpRequest req){

 HttpResponse res = req.response;
 addCorsHeaders(res);
 print('${req.method}: ${req.uri.path}');

 Future future() => new Future.value(true);

 req.listen((List<int> buffer) {
 var theDataLines = JSON.decode(new String.fromCharCodes(buffer));
 print(theDataLines);
        connect(db).then((conn) {  
          for (var theData in theDataLines)
               conn.execute("""
                      insert into pol 
                             (poid,ponum,lnum,itmid,icode,qty,rqty,bqty,iqty,biqty,price,rdd, eta, flag)
                      values (@poid,@ponum,@lnum,@itmid,@icode,@qty,@rqty,@bqty,@iqty,@biqty,@price,
                              to_timestamp(@rdd,'YYYY-MM-DD'), to_timestamp(@eta,'YYYY-MM-DD'), @flag)
                   """,
                {                     
                      'poid': theData['poid'],
                     'ponum': theData['ponum'],
                     'lnum' : theData['lnum'],
                    'itmid' : theData['itmid'],
                    'icode' : theData['icode'],
                       'qty': theData['qty'],
                      'rqty': theData['rqty'],
                      'bqty': theData['bqty'],
                      'iqty': theData['iqty'],
                     'biqty': theData['biqty'],
                     'price': theData['price'],
                       'rdd': theData['rdd'].toString(),
                       'eta': theData['eta'].toString(),
                      'flag': theData['flag']
                 })
        .then((_)=>conn.query('commit').toList().then((rows) {print('committed');}))
        .then((_){
                    res.write('done');
                    res.close(); 
               });
    });    // END of SQL
  }, onError: printError);  // End of server listen
 }  // END of function

I even tried to change the:

case '/sPO': savePO(req); break;

to be

case '/sPO': print(1); break;

the it printed the 1, 4 times after sending an order of 6 lines!!


Solution

  • It's hard to see for me what you actually try to accomplish.

    The problem is very probably your save() method. You wrote how it behaves but not much about what you try to accomplish?
    Why don't you put more lines into one JSON string and post them together in one request?

    You create one request instance and call send repeatedly on this one request instance. You also register the onReadyStateChange handler more than once on the same request object which results in onData_save being called several times when the event occurs just once.

    I think you should either move request = new HttpRequest(); down just before

    request.open('POST', host+'/sPO');
    request.send(JSON.encode(orderLines));  
    

    or better move request.onReadyStateChange.listen(onData_save); up to request = new HttpRequest();, add all orderlines into one JSON and call

    request.open('POST', host+'/sPO');
    request.send(JSON.encode(orderLines));  
    

    after the for loop.

    Another problem I see is that you do a fire and forget. What if the send request fails for some reason?

    I would create a sendJSON method that returns a future (with a Completer which completes on onDone and completeError when something goes wrong.

    When you want to create more than one request in your save() you can use something like

    // create your JSON
    
    var futures = [];
    for(i = 0; i < 5; i++) {
      futures.add(sendData(myJson)); // collects the futures returned from sendData
    }
    // executes all futures and waits for all to respond and then returns another future
    return Future.wait()
    .then((results) {
      results.forEach((r) {
        // check result
      });
    });