Use Future when retrieving data using HttpRequest in Dart - dart

At the moment I'm using a simple HttpRequest to retrieve a JSON file:
void getConfigData(String url) {
var request = new HttpRequest.get(url, this.onSuccess);
}
void onSuccess(HttpRequest req) {
JsonObject conf = new JsonObject.fromJsonString(req.responseText);
MenuItemCollection top_bar = new MenuItemCollection();
// and parse the JSON data ...
}
What I would like to know is if I should be using Futures instead of the callback?

You don't really have the choice between Futures or callbacks, this choice is made by the API you are using. Sometimes you have to give a callback like with HttpRequest.get and sometimes you get a Future like File.create.

Related

Reading a POST request in Undertow without consuming it

In Undertow I have two handlers, that are chained:
The first handler reads the request and then calls calls the second handler via next.handleRequest(exchange);
The second handler is a proxy handler which send the request to and external server where it is processed.
My problem is the first handler which reads the request. The request headers are no big deal but getting the body data of POST requests is a problem.
Existing solutions as shown in the question How to properly read POST request body in a Handler? consume the request body su that the handler chaining does not work anymore.
How can I read the request body data without consuming it or altering the request in a way that the handler chain does not work afterwards?
I found the problem, in the end it was a missing call to ByteBuffer.flip().
If someone ever needs such an POST data reader one can use the following simplified implementation of an AbstractStreamSourceConduit that is able to read the incoming POST data without consuming it:
exchange.addRequestWrapper(new ConduitWrapper<StreamSourceConduit>() {
#Override
public StreamSourceConduit wrap(ConduitFactory<StreamSourceConduit> factory, HttpServerExchange exchange) {
StreamSourceConduit source = factory.create();
return new AbstractStreamSourceConduit<StreamSourceConduit>(source) {
ByteArrayOutputStream bout = new ByteArrayOutputStream(8192);
#Override
public int read(ByteBuffer dst) throws IOException {
int x = super.read(dst);
if (x >= 0) {
ByteBuffer dup = dst.duplicate();
dup.flip();
byte[] data = new byte[x];
dup.get(data);
bout.write(data);
} else {
// end of stream reached
byte[] data = bout.toByteArray();
// ... to something with data
}
return x;
}
};
}
});

Async tast not working as it should

I am trying to do webservice async so ui can respond and websevice call is done in background, but since i am little inexpirienced with async stuff i need little help.
Here is my code:
Inside my action result i have call to method which have asyinc stuff in it
public ActionResult SavePackage(string ParcelNumber)
{
/////some other stuff
SaveAsync(ParcelNumber);
}
And
then i have async method :
public async Task SaveAsync(string ParcelNumber)
{
await api.RegisterPackage(ParcelNumber);
}
Which calls api:
public async Task RegisterPackage(string ParcelNumber)
{
var rk = "some api http";
HttpWebRequest request = WebRequest.Create(rk) as HttpWebRequest;
request.Headers.Add("cache-control", "no-cache");
request.Method = "POST";
request.ContentType = "application/json";
string data = "{\n \"ParcelNumber\": \"" + ParcelNumber+ "\"}";
byte[] dataStream = Encoding.UTF8.GetBytes(data);
Stream newStream = request.GetRequestStream();
// Send the data.
newStream.Write(dataStream, 0, dataStream.Length);
newStream.Close();
Task<WebResponse> getResponseTask = Task<WebResponse>.Factory.FromAsync(request.BeginGetResponse, request.EndGetResponse, null);
await getResponseTask.ContinueWith(getResponseAntecedent =>
{
WebResponse webResponse = getResponseAntecedent.Result;
using (var reader = new StreamReader(webResponse.GetResponseStream()))
{
string error = reader.ReadToEnd();
//TODO: use JSON.net to parse this string and look at the error message
var myDeserializedObjList3 = Newtonsoft.Json.JsonConvert.DeserializeObject<RootObjectAtt>(error);
var isValid = myDeserializedObjList3.IsValid;
var ModelErrors = myDeserializedObjList3.ModelErrors;
var ValidationErrors = myDeserializedObjList3.ValidationErrors;
}
});
}
My problem is that the above code is still waiting for response to finish (and that can take about 20 second), and i would like if possible when i call the api i could go back to my ui and let ppl do their stuff while api is proccessed in background.
Anybody have any suggestion?
As I describe on my blog, async does not change the HTTP protocol.
First, the best solution to your problem is to not change your ASP.NET action method at all. Instead, have the actual UI application issue the call asynchronously. If your UI app is a .NET app, then it can use async/await; if it's a web app (i.e., JavaScript), then it can use an AJAX call. Another good option is to introduce SignalR, which allows the server to signal when the work is done.
If you really want to run ASP.NET code outside of a request (which is never recommended), then you can use one of the techniques I describe on my blog for ASP.NET fire-and-forget.

Retrieving the response body from an HttpClientResponse

I'm trying to write some tests for my Dart server application, and I've been using the HttpClient class (along with the related HttpClientRequest and HttpClientResponse classes to make test requests to the server (note that I'm using these classes because I need the dart:io package for running the server, so I can't also import dart:html). This has been going fairly well so far, and I've been able to write tests to check that the server is returning responses with the correct HTTP Status code. The base of the code I've been using to make these test calls is as follows:
Future<HttpClientResponse> makeServerRequest(String method, Uri uri, [String jsonData]) async {
HttpClient client = new HttpClient();
HttpClientRequest request = await client.openUrl(method, uri);
request.write(jsonData);
return request.close();
}
Now I need to write a test that makes sure that the body of the response, not just the status code, is correct. The problem is that I can't seem to find anything that allows me to actually access the response body in the HttpClient* classes. The closest I've been able to find so far is the HttpClientResponse.contentLength property, but that only tells me how big the response body is, and isn't the actual content.
How do I retrieve the response body from these requests? Or, if you aren't able to, is there some other way I can make the requests on a server side application so I can read the responses?
The HttpClientResponse object is a Stream, so you can just read it with the listen() method:
response.listen((List<int> data) {
//data as bytes
});
You can also use the codecs from dart:convert to parse the data. The following example reads the response contents to a String:
import 'dart:io';
import 'dart:convert';
import 'dart:async';
Future<String> readResponse(HttpClientResponse response) {
final completer = Completer<String>();
final contents = StringBuffer();
response.transform(utf8.decoder).listen((data) {
contents.write(data);
}, onDone: () => completer.complete(contents.toString()));
return completer.future;
}
Low level
Here is the await for version of collecting the response stream. It's a little more compact than using a completer.
Future<String> readResponse(HttpClientResponse response) async {
final contents = StringBuffer();
await for (var data in response.transform(utf8.decoder)) {
contents.write(data);
}
return contents.toString();
}
You should wrap it in a try catch block to handle errors.
High level
Most of the time from the client side you would use the http library instead:
// import 'package:http/http.dart';
Response response = await get(url);
String content = response.body;
See this article for more details.
A short way of getting the body from an HttpClientResponse is:
Future<String> readResponse(HttpClientResponse response) async {
return response.transform(utf8.decoder).join();
}

how to simply fetch an url body as a string synchronously

How would you simply implement this function:
String fetchUrlBodyAsString(String url) {
...
}
Usage:
String schema = fetchUrlBodyAsString("http://json-schema.org/draft-04/schema#");
This thread Using dart to download a file explains a good way to get to the data from a main function. But if you try it you see that the real work happens after leaving main. I think that the synchronous function that I want to create is difficult using HttpClient because it is trying to get an async api to work synchronously. According to this thread that may not be possible: https://groups.google.com/a/dartlang.org/d/msg/misc/kAgayQyaPhQ/wonJ776_FGIJ
What is a Dart way to implement this in a non-browser/console setting?
The using of asynchronous methods is really infectious. Once you start using Future inside a function you have to return a Future as result. So your fetchUrlBodyAsString function can look like :
import 'dart:io';
import 'dart:async';
Future<String> fetchUrlBodyAsString(String url) =>
new HttpClient().getUrl(Uri.parse(url))
.then((HttpClientRequest request) => request.close())
.then((HttpClientResponse response) =>
response.transform(new StringDecoder()).join());
main() {
final url = "http://json-schema.org/draft-04/schema#";
Future<String> schema = fetchUrlBodyAsString(url);
schema.then(handleContent);
}
handleContent(String content) {
print(content); // or do what you want with content.
}
or with async/await:
import 'dart:io';
import 'dart:async';
Future<String> fetchUrlBodyAsString(String url) async {
var request = await new HttpClient().getUrl(Uri.parse(url));
var response = await request.close();
return response.transform(new StringDecoder()).join();
}
main() async {
final url = "http://json-schema.org/draft-04/schema#";
handleContent(await fetchUrlBodyAsString(url));
}
handleContent(String content) {
print(content); // or do what you want with content.
}
You can make synchronous http requests by using HttpRequest.open and setting async to false.
https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-dom-html.HttpRequest#id_open
import 'dart:html';
String fetchUrlBodyAsString(String url) {
var request = new HttpRequest()
..open('GET', url, async: false)
..send();
return request.responseText;
}
String schema = fetchUrlBodyAsString("http://json-schema.org/draft-04/schema#");
There is no way to turn an async API into a sync API; once you have a Future as a result, that is what you will have to deal with.
For your specific example, the only way to achieve what you want would be to build your own synchronous HTTP library from the ground up. Using asynchronous APIs in a synchronous manner is not possible.

How to get a value back from an HttpRequest?

I'm learning Dart and I've hit a roadblock. I very much want to return a value from a json string processing function so I can use that value inside main(). (I'm trying to set some top-level variables to use with a one-way data bind with an html template.) I'm using HttpRequest.getString and a .then call to kick off the processing. But HttpRequest doesn't like being assigned to a variable so I'm not sure how to get anything back out of it.
processString(String jsonString) {
// Create a map of relevant data
return myMap;
}
void main() {
HttpRequest.getString(url).then(processString);
// Do something with the processed result!
}
I guess my question is how do I get a value back from a function that was called from an HttpRequest?
You're trying to do something that the Dart async model doesn't support. You'll have to handle the result of the async request:
In processString(),
In another function called from processString(),
In an anonymous function passed to then().
Or something similar. What you can't do is access it from further down in main():
processString(String jsonString) {
// Create a map of relevant data
// Do something with the processed result!
}
void main() {
HttpRequest.getString(url).then(processString);
// Any code here can never access the result of the HttpRequest
}
You may prefer:
processString(String jsonString) {
// Create a map of relevant data
return myMap;
}
void main() {
HttpRequest.getString(url).then((resp) {
map = processString(resp);
// Do something with the processed result!
});
// Any code here can never access the result of the HttpRequest
}

Resources