3
votes

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
I tried your code and it runs without an error. asBroadcastStream() was not necessary. - Günter Zöchbauer
I guess you are also listening in your one of your filter handlers. - Günter Zöchbauer
The niether filter has listens in it. I thought that ..serve(..).listen(userPostHandler) was causing the problem. Is this not the case? - coryrwest
Yes but as it seems because somewhere else is already listening. I couldn't get asBroadcastStream() working. Your error is only reproducible when I also add a listener to a filter handler. - Günter Zöchbauer
I added the filters I am using. I am not sure if anything in there as an implicit listener. Any insights? - coryrwest

2 Answers

1
votes

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

0
votes

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.