Search code examples
dartdart-iodart-async

Dart HTTP server and Futures


I am trying to write simple HTTP server which parse result of client.getUrl(). I've got everything working except I am not able to write back to http request object (print to console works fine).

Relevant code is:

main() {
  HttpServer.bind(InternetAddress.ANY_IP_V4, 4040)
      .then((HttpServer server) {
    print('listening on localhost, port ${server.port}');
    server.listen((HttpRequest request) {
      Future loadedContent = loadUrl(furl);
      loadedContent.then((content) => print(content));
  //  loadedContent.then((content) => request.response.write(content));

      request.response.close();
      print ("response closed");
    });
  }).catchError((e) => print(e.toString()));
}

Problem is that main function ends before I get result from Future (figured that out by printing "response closed" which appears second before results). Is there way to wait for results in main function?

Edit: It is related to Dart HttpRequest return future I will rewrite my code, but if there is a way to wait for getUrl Future in main function, I would prefer it.

Edit: my loadUrl

Future loadUrl(String url)
{
  final c = new Completer();
  HttpClient client = new HttpClient();
  client.addCredentials(
          Uri.parse("https://*****.tpondemand.com/api"),
          "tprealm",
          new HttpClientBasicCredentials("*****", "*****"));
  client.getUrl(Uri.parse(url))
      .then((HttpClientRequest request) {
        // Optionally set up headers...
        // Optionally write to the request object...
        // Then call close.

        return request.close();
      })
      .then((HttpClientResponse response) {
        // Process the response.
        //print(response.reasonPhrase);
        response.transform(UTF8.decoder).listen((contents) {
             // handle data
            Map parsedMap = JSON.decode(contents);
            c.complete(parsedMap);
            //req.response.write(parsedMap["Items"][0]);
           });

      });
  return c.future;
  }

last edit: this is working code

import 'dart:io';
import 'dart:async';
import 'package:http_server/http_server.dart';
import 'dart:convert';

final furl = "https://***.tpondemand.com";

Future loadUrlBody(String url) {
  final c = new Completer(); 
  HttpClient client = new HttpClient();
  client.addCredentials(Uri.parse("https://***.tpondemand.com/api"), "tprealm", new HttpClientBasicCredentials("user", "password"));
  client.getUrl(Uri.parse(url))
      .then((HttpClientRequest response) => response.close())
      .then(HttpBodyHandler.processResponse)
      .then((HttpClientResponseBody body) {
       c.complete(body);
      });
  return c.future;
}
main() {
  final filter = "/api/v1/Userstories?format=json&where=(Team.Id+eq+111111)&include=[Name,CreateDate,ModifyDate,LastCommentDate]&take=1000";
  HttpServer.bind(InternetAddress.ANY_IP_V4, 4040).then((HttpServer server) {
    print('listening on localhost, port ${server.port}');
    server.listen((HttpRequest request) {
      print(request.connectionInfo.remoteAddress);
      loadUrlBody(furl + filter).then((content) {
        Map parsedMap = content.body;
        //print("Map parsed");
        request.response.write(parsedMap["Items"]);
        request.response.close();
        //print("response closed");
      }).catchError((e) => print(e.toString()));
    });
  }).catchError((e) => print(e.toString()));
}

Solution

  • When your loadUrl() returns a Future (which it probably should) then this should work

    main() {
      HttpServer.bind(InternetAddress.ANY_IP_V4, 4040)
          .then((HttpServer server) {
        print('listening on localhost, port ${server.port}');
        server.listen((HttpRequest request) {
          loadUrl(furl).then(() {
            // loadedContent.then((content) => print(content));
            loadedContent.then((content) => request.response.write(content));
    
            request.response.close();
            print ("response closed");
          });
        });
      }).catchError((e) => print(e.toString()));
    } 
    

    update

    You need to modify your getData() or loadUrl() method

    Future getData(HttpRequest request) { // added return type 'Future' (not necessary)
      return dao.findAll().then((value) { // added 'return'
        print(value);
      });
    }
    

    update 2

    Future loadUrl(String url)
    {
      // final c = new Completer(); // <== commented out
      HttpClient client = new HttpClient();
      client.addCredentials(
              Uri.parse("https://*****.tpondemand.com/api"),
              "tprealm",
              new HttpClientBasicCredentials("*****", "*****"));
      return client.getUrl(Uri.parse(url))                      // <== added return
          .then((HttpClientRequest request) {
            // Optionally set up headers...
            // Optionally write to the request object...
            // Then call close.
    
            return request.close();
          })
          .then((HttpClientResponse response) {
            // Process the response.
            //print(response.reasonPhrase);
            return response.transform(UTF8.decoder).listen((contents) { // <== added return
                 // handle data
                Map parsedMap = JSON.decode(contents);
                // c.complete(parsedMap); // <== commented out
                //req.response.write(parsedMap["Items"][0]);
               }).asFuture();  // <== added `.asFuture()`
    
          });
       // return c.future; // <== commented out
    }
    

    Normally it should be sufficient to prepend a return before each call to a method that returns a Future, then you can avoid using a completer. Completer are only for more complex situations (for example when you return the completer.future from one method but complete it from somewhere else, for example an event handler).