Handle POST data using Dart Route after already listening to stream

544 views Asked by At

I am using route to handle http requests to my server. This is my current route code:

HttpServer.bind("127.0.0.1", 8080).then((server) {
    new Router(server)
    ..filter(new RegExp(r'/.*'), addCorsHeaders)
    ..filter(new RegExp(r'/admin/.*'), authenticate)
    ..serve(userGetURL, method: 'GET').listen(userGetHandler)
    ..serve(userPostURL, method: 'POST').listen(userPostHandler);
}); 

I am trying to get JSON data that I am POSTing to a URL. The data will be used to get an entity from the database and return it as JSON to the caller. I am basically trying to create a server application that will handle all the data and a client application that will display it.

I cannot figure out how to get the data from a POST. Everything I have tried requires that I listen to the stream, but it is already being listened to. This is how I have been trying to get the POST data:

userPostHandler(HttpRequest req) {
    req.listen((List<int> buffer) {
        // Return the data back to the client.
        res.write(new String.fromCharCodes(buffer));
        res.close();
    }
}

The problem is I get a Bad state: Stream has already been listened to. error.

EDIT: The filters

Future<bool> authenticate(HttpRequest req) {
  if (req.method == 'POST') {
    // Post data is not null
    // Authenticate user
    String userName = '';
    String password = '';
    User user = new User();
    user.DBConnect().then((User user) {
      return new Future.value(user.ValidateUser(userName, password));
    });
  }
}

Future<bool> addCorsHeaders(HttpRequest req) {
  print('${req.method}: ${req.uri.path}');
  req.response.headers.add('Access-Control-Allow-Origin', '*, ');
  req.response.headers.add('Access-Control-Allow-Methods', 'POST, OPTIONS, GET');
  req.response.headers.add('Access-Control-Allow-Headers',
      'Origin, X-Requested-With, Content-Type, Accept');
  return new Future.value(true);
}
2

There are 2 answers

0
coryrwest On BEST ANSWER

Using the following code I was able to get a POST working:

void main() {
    HttpServer.bind("127.0.0.1", 8080).then((server) {
        new Router(server)
        ..filter(new RegExp(r'/.*'), addCorsHeaders)
          ..filter(new RegExp(r'/admin/.*'), authenticate)
           ..serve(userGetURL, method: 'GET').listen(userGetHandler)
            ..serve(userPostURL, method: 'POST').listen(userPostHandler);
    }); 
}

Future userPostHandler(HttpRequest req) {
  bool headerSent = false;
  // Start listening before writing to the response.
  req.listen((List<int> buffer) {
    if (!headerSent) {
      req.response.write("User POST");
      headerSent = true;
    }
    req.response.write(new String.fromCharCodes(buffer));
    },
    // Use onDone to close the response.
    onDone: () => req.response.close()
  );
}

Here is what I figured out. Any write to the response automatically drains the body and thus destroy the POST data. As mentioned here. Also, listening to the response is done asynchronously and thus must be completed before close() is called.

3
Günter Zöchbauer On

I have never used the Route package but I wonder why you want to listen inside the Handler. Can't you just access the properties you want to process?

Otherwise you could try

req.asBroadcastStream().listen(...)

A BroadcastStream supports multiple listeners. More information in this article Use Streams for Data