I used to do something like this:
HttpResponse res = req.response;
String dataReceived;
await req.listen((List<int> buffer) {
dataReceived = new String.fromCharCodes(buffer);
}).asFuture();
Map data = JSON.decode(dataReceived);
When I needed UTF8 support, I modified it to:
Map data = JSON.decode(await new Utf8Codec().decodeStream(request));
Kevin Moore suggested to encode/decode like this:
https://dartpad.dartlang.org/1d229cfdc1c1fd2ab877
So I've got:
Map data;
await request.listen((List<int> buffer) {
data = JSON.fuse(UTF8).decode(buffer);
}).asFuture();
Not sure that I need the asFuture():
Map data;
await request.listen((List<int> buffer) => data = JSON.fuse(UTF8).decode(buffer));
Or do I? And this method requires that I encode it into bytes on the client side:
sendData: new JsonUtf8Encoder().convert({'model': message, 'authToken': app.authToken}))
What are the benefits of this? Isn't it more to send over the wire?
I believe Shelf and/or the new RPC lib would handle this stuff for me? Shall I move to one of those? Right now, it's all homegrown.
HttpRequest is a Stream<List<int>>. You don't want to use listen because you'll only get the first "chunk" of data.
Instead you'll want to do something like this:
import 'dart:async';
import 'dart:convert';
main() async {
var input = {'a':1, 'b':2};
var decoder = JSON.fuse(UTF8).decoder;
var json = await decoder.bind(toByteStream(input)).single;
print(json);
}
Stream<List<int>> toByteStream(json) =>
_encoder.bind(new Stream.fromIterable([json]));
final _encoder = new JsonUtf8Encoder();
https://dartpad.dartlang.org/9807d0c5ed89360c9f53
Yes as you can see on https://github.com/dart-lang/shelf/blob/master/lib/src/message.dart#L136 shelf defaults to UTF-8
I am likely biased but I would definitely recommend moving over to shelf. You have several options depending on what you prefer, like:
shelf_rpc as you mentioned. I haven't used it but likely full featured API support
shelf_bind if you simply want to bind a handler function parameter to a JSON body. This is lower level, more flexible and less prescriptive but does less. e.g.
router.post('/foo', (#RequestBody() Foo foo) => ...)
shelf_rest. Adds higher level more prescriptive API support (similar to shelf_rpc).
full frameworks like redstone, mojito etc. These do more for you but you need to buy into more
Had a chat w/ Kevin to better understand his answer, and thought it best to share my learnings as a new answer.
HttpRequest is always a Stream<List<int>> – a streamed list of integers. Those integers are bytecodes, and this is commonly referred to as a bytestream. You can be sure that no matter what API you use to send data over the wire, that it is sent as a bytestream.
The HttpRequest.request() method accepts sendData in several forms...
* If specified, `sendData` will send data in the form of a [ByteBuffer],
* [Blob], [Document], [String], or [FormData] along with the HttpRequest.
Source:
https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart:html.HttpRequest#id_request
...but these are just abstractions, and ultimately your data is sent as a Stream<List<int>> bytestream.
So on the server we first set up a decoder that will decode both JSON and UTF8 (for correct char handling), and then we bind that to the HttpRequest request, which is a bytestream. I think single just serves to ensure we throw an exception if we received more than one data event. Here's all the code we need to interpret an HttpRequest:
import 'dart:async';
import 'dart:convert';
static handleRequest(HttpRequest request) async {
var decoder = JSON.fuse(UTF8).decoder;
var data = await decoder.bind(request).single;
print('The decoded data received is:\n\n$data');
}
Related
I try to implement an API using the RPC dart package.
In all the example I found, response are build manually (ie new Response()..message = "hello").
In my case, i read JSON data from mongodb and want to return them with minimal transformation (basically picking only external properties).
The fromRequest of the method schema can be used to do this :
class QueryResult {
//my props
}
#ApiMethod(path: "myMongoQuery")
Future<List<QueryResult>> myMongoQuery() async {
var schema = _server.apiMap["/query/v1"].schemaMap["QueryResult"];
var results = await coll.find();
return results.map(schema.fromRequest).toList();
}
The problem in my code is the first line (_server.apiMap["/query/v1"].schemaMap["QueryResult"]) it's a pure hack to retrieve the schema of my method.
I tried to use mirror to retrieve the schema in an elegant/generic way but did not succeed.
Anyone can help me on this ?
Cheers,
Nicolas
I have requested a binary file from the server and I want to de-serialize the data from this file into class objects. Below is my code. Could some one tell me if there is a more elegant and convenient way to do this for I have several different class objects.
import 'dart:html';
import 'dart:typed_data';
class Header {
final ByteData magic = new ByteData(3);
int version;
int minorVersion;
...// Other data members.
}
void main() {
var path = 'url to a binary file';
var request = new HttpRequest();
request
..open('GET', path)
..responseType = 'arraybuffer'
..onLoadEnd.listen((e) => requestComplete(request))
..send();
}
void requestComplete(HttpRequest request) {
if (request.status == 200) {
print('headers: ${request.responseHeaders}');
print('type: ${request.responseType}');
ByteBuffer byteBuffer = request.response;
var magic = byteBuffer.asUint8List(0, 3);
print(new String.fromCharCodes(magic));
var data = byteBuffer.asInt32List(4, 8);
print(data);
...// Process other data
} else {
print('Request failed, status = ${request.status}');
}
}
The binary is dumped from another software. In some object oriented language, like c/c++, c#, java, etc, there are some kinds to methods to read back/de-serialize the data structures. But I didn't find a way in javascript and dart yet
This sounds like a language specific encoding. There usually aren't libraries available for other languages to deserialize because the source language probably uses features/constructs that aren't available in the target language or can't be mapped 1:1.
If you can use a platform-neutral way to serialize in the first place there are several options
JSON is used for this a lot, because it it language-independent but also somewhat limited.
There are other cross-platform solutions like Googles Protocol Buffers and a lot of others with support for lots of programming languages.
You can of course decode the binary data in Dart, but you need to know how to interpret the binary data yourself and read and interpret it accordingly.
I am using Spray to query a REST endpoint which will return a largish amount of data with several items that should be processed. The data is a series of json objects. Is there a way to convert the response into a stream of these objects that doesn not require me to read the entire response into memory?
Reading the docs there is mention of "chunked responses", which seem to be along the lines of what I want. How do I use that in a spray-client pipeline?
I've just implemented something like this today, thanks to the excellent article found at http://boldradius.com/blog-post/VGy_4CcAACcAxg-S/streaming-play-enumerators-through-spray-using-chunked-responses.
Essentially, what you want to do is to get hold of the RequestContext in one of your Route definitions, and get a reference to its "responder" Actor. This is the Actor by which Spray sends responses back to the client that sent the original request.
To send back a chunked response, you have to signal that the response is starting, then send the chunks one by one, and then finally signal that the response has finished. You do this via the ChunkedResponseStart, MessageChunk, and ChunkedMessageEnd classes from spray.http package.
Essentially what I end up doing is sending a response as a series of these classes like this:
0) A bunch of imports to put into the class with your Routes in, and a case object:
import akka.actor.{Actor, ActorRef}
import spray.http._
import akka.actor.ActorRef
import akka.util.Timeout
import akka.pattern.ask
import spray.http.HttpData
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future}
import akka.actor.{ActorContext, ActorRefFactory, Props}
import spray.http.{HttpData, ContentType}
import spray.routing.RequestContext
import scala.concurrent.ExecutionContext
import scala.concurrent.ExecutionContext.Implicits.global
import spray.json.RootJsonFormat
import spray.http.MediaTypes._
object Messages {
case object Ack
}
1) Get a hold of the requestContext from your Route:
path ("asdf") {
get { requestContext => {
... further code here for sending chunked response ...
}
}
2) Start the response (as a JSON envelope that'll hold the response data in a JSON array called "myJsonData" in this case):
responder.forward(ChunkedResponseStart(HttpResponse(entity = HttpEntity(`application/json`, """{"myJsonData": ["""))).withAck(Ack))
3) Iterate over your array of results, sending their JSONified versions to the response as elements in the JSON array, comma separated until the final element is sent - then no need for a trailing comma:
requestContext.responder.forward(MessageChunk(HttpData(myArray.toJson).withAck(Ack))
if (!lastElement) { // however you work this out in your code!
requestContext.responder.forward(MessageChunk(HttpData(",").withAck(Ack))
}
4) When there's nothing left to send, close the JSON envelope:
responder.forward(MessageChunk("]}").withAck(Ack))
and signal the end of the response:
responder.forward(ChunkedMessageEnd().withAck(Ack))
In my solution I have been working with Play Iteratees and Enumerators and so I have not included big chunks of code here because they are very much tied up with these mechanisms which may not be suitable for your needs. The point of the "withAck" call is that this will cause the responder to ask for an Acknowledgement message when the network signals that it's OK to accept more chunks. Ideally you would craft your code to wait for the return of the Ack message in the future before sending more chunks.
I hope that the above may give you a starter for ten at least, and as I say, these concepts are explained really well in the article I linked to!
Thanks,
Duncan
Problem:
I have a dart file defining some data structures, which I need to use both for the client and for the server. I'd like to make these data structures observable with Polymer. However, the server cannot include the file because of Polymer, because Polymer includes dart:html.
Context:
I am working on a client/server (REST-full) application, where I want the server to provide the data structures defined available as resources. The client should display these resources, and have the possibility to send the modifications to the server. For that, Polymer is invaluable.
The reason I want to have this library available for the server is that I want the server to be able to validate the resources to be stored.
Possible solutions:
I don't yet know the internals of Polymer enough, but if my data structures could inherit from Map, I could use toObservable in the client side code to make the data structure observable, but instead of accessing by dot notation, I'd have to access members by keys instead, making it rather fragile.
I was wondering if I could use mirrors.dart to add the observable annotation on the client.
Of course, managing duplicate code, is really not a solution.
You can use the observe package.
With ChangeNotifier you initiate the change notification yourself by calling notifyPropertyChange when a value changes. The changes get delivered synchronously.
Observable needs dirtyCheck() to be called to deliver changes.
Polymer calls Observable.dirtyCheck() repeatedly to get the changes automatically.
an example for each
import 'package:observe/observe.dart';
class Notifiable extends Object with ChangeNotifier {
String _input = '';
#reflectable
get input => _input;
#reflectable
set input(val) {
_input = notifyPropertyChange(#input, _input, val + " new");
}
Notifiable() {
this.changes.listen((List<ChangeRecord> record) => record.forEach(print));
}
}
class MyObservable extends Observable {
#observable
String counter = '';
MyObservable() {
this.changes.listen((List<ChangeRecord> record) => record.forEach(print));
}
}
void main() {
var x = new MyObservable();
x.counter = "hallo";
Observable.dirtyCheck();
Notifiable notifiable = new Notifiable();
notifiable.input = 'xxx';
notifiable.input = 'yyy';
}
With the release of the new dart:io libraries, in order to read the data from an HttpRequest we now need to:
Listen to the body to handle data and notified of it's completion.
In order to accomplish this, I've been using something similar to the following to get all of the data from the request (taken from the dart:io tests):
List<int> body = new List<int>();
request.listen(body.addAll, onDone: () {
var str = new String.fromCharCodes(body);
// Now do something with the string of data
});
Is there some way of using transform and/or StringDecoders to already provide a constructed result. The only way I can think of, I would still need to create a StringBuffer and use writeAll to ensure all data is passed in the event it doesn't all arrive at once, and still call the onDone before using the string in the buffer.
So I guess ultimately the question is: is there some way I can use a HttpRequest (or in all actuality any Stream) without needing to buffer/build the results and can just pass a callback or handler which receives the entire contents?
I haven't tried this, but I think you should be able to use StringDecoder, toList() and join() to get the whole body:
request.transform(new StringDecoder()).toList().then((data) {
var body = data.join('');
print(body);
}