How do i read HttpRequest data sent by POST method from client, on the server, in Dart?
I send a message from the client like this:
HttpRequest request = new HttpRequest();
var url = "http://127.0.0.1:8081";
request.open("POST", url, async: false);
String data = 'hello from client';
request.send(data);
On server i am catching the request like this:
HttpServer.bind('127.0.0.1', 8081).then((server) {
server.listen((HttpRequest request) {
//DATA SHOULD BE READ HERE
});
});
But i cant figure out how to actually read the data... There is not data property in HttpRequest nor anything else...
EDIT This is how i get the answer now:
HttpServer.bind('127.0.0.1', 8081).then((server) {
server.listen((HttpRequest request) {
//DATA SHOULD BE READ HERE
print("got it");
print(request.method);
if(request.method == "POST") {
print("got it 2");
List<int> dataBody = new List<int>();
request.listen(dataBody.addAll, onDone: () {
var postData = new String.fromCharCodes(dataBody);
print(postData);
});
}
});
});
But for some reason the request.method is not "POST" but "OPTIONS", and if i change to if(request.method == "OPTIONS") , then print(postData) will still return nothing...
You can use the StringDecoder to tranform from "List of Int" to "String" from the HttpRequest. Since no matter if you send json, plain text, or png, Dart always send data in form of
"List of Int" to the server.Another means is to use the Streams (http://www.dartlang.org/articles/feet-wet-streams/) tested on Heroku Steam v0.6.2 Dart Editor 0.4.3_r20602 Dat SDK 0.4.3.5_r26062
For example,
the client:
import 'dart:html';
import 'dart:json' as Json;
import 'dart:async';
import 'dart:uri';
final String data = 'Hello World!';
void _sendPNG(String pngData) {
HttpRequest request = new HttpRequest(); // create a new XHR
// add an event handler that is called when the request finishes
request.onReadyStateChange.listen((_)
{
if (request.readyState == HttpRequest.DONE &&
(request.status == 200 || request.status == 0)) {
// data saved OK.
print(request.responseText); // output the response from the server
}
}
);
// POST the data to the server Async
print('Sending Photos to the server...');
var url = "/png";
request.open("POST", url);
request.setRequestHeader("Content-Type", "text/plain");
request.send(data);
}
the server:
import 'dart:io';
import 'dart:async';
import 'dart:json' as Json;
import "package:stream/stream.dart";
import 'package:xml/xml.dart' as xml;
import 'package:unittest/unittest.dart';
import 'package:rikulo_commons/mirrors.dart';
void receivePNG(HttpConnect connect){
var request = connect.request;
var response = connect.response;
if(request.uri.path == '/png' && request.method == 'POST')
{
String png='';
response.write('The server received png request!');
//read incoming List<int> data from request and use StringDecoder to transform incoming data to string
var stream = request.transform(new StringDecoder());
stream.listen((value){
print(value);
//Hello World!
}
else
{
response.write('error');
response.statusCode = HttpStatus.NOT_FOUND;
connect.close();
}
}
configure.dart
var _mapping = {
"/": home,
"/png": receivePNG,
};
Right now, the handling of POST data is a little difficult. But essentially the HttpRequest itself has to be 'listened' to. HttpRequest is a stream itself. In particular it's a Stream<List<int>>. So basically your data may be passed to your HttpRequest as multiple List<int>'s. So we need to reconstruct the data then convert it into a string (assuming you're expecting a string, not binary data, etc). Here's more or less what I do:
HttpServer.bind('127.0.0.1', 8081).then((server) {
server.listen((HttpRequest request) {
if(request.method == "POST") {
List<int> dataBody = new List<int>();
request.listen(dataBody.addAll, onDone: () {
var postData = new String.fromCharCodes(dataBody);
// Do something with the data now.
});
}
request.response.close();
});
Note that the request.listen(dataBody.AddAll, ...) basically calls List.addAll() each time data is to the server (in cases of larger data or multi-part forms it may not come all at once). This ensures we buffer it all until the stream indicates it is 'done' In which case we can now do something with the data we received, like convert it to a string.
I have found this useful example with client/side code
GitHub json send to server Example
// XXX: Dart Editor thinks this is OK, but I haven't run it.
import 'dart:html';
String encodeMap(Map data) {
return data.keys.map((k) {
return '${Uri.encodeComponent(k)}=${Uri.encodeComponent(data[k])}';
}).join('&');
}
loadEnd(HttpRequest request) {
if (request.status != 200) {
print('Uh oh, there was an error of ${request.status}');
return;
} else {
print('Data has been posted');
}
}
main() {
var dataUrl = '/registrations/create';
var data = {'dart': 'fun', 'editor': 'productive'};
var encodedData = encodeMap(data);
var httpRequest = new HttpRequest();
httpRequest.open('POST', dataUrl);
httpRequest.setRequestHeader('Content-type',
'application/x-www-form-urlencoded');
httpRequest.onLoadEnd.listen((e) => loadEnd(httpRequest));
httpRequest.send(encodedData);
}
Related
I have several URL similar to https://zkillboard.com/api/stats/solarSystemID/31000007/
I am trying to extract the JSON from the url into an object.
I have been able to get as far as this which returns a Promise, PromiseState: fulfilled and PromiseResults contains an object with the data I am looking for.
async function readJSON(url:string) {
var request = new XMLHttpRequest();
request.open ('get', url, false)
request.send(null)
if (request.status == 200) {
return JSON.parse(request.responseText)
}
}
const systemJSON = readJSON('https://zkillboard.com/api/stats/solarSystemID/31000007/')
console.log(systemJSON)
How can I ensure that my console.log only returns the PromiseResult?
This seems to have fixed it for me, removed the async from the function as well as .responseText in the JSON.parse()
function readJSON(url:string) {
var request = new XMLHttpRequest();
request.open ('get', url, false)
request.send(null)
if (request.status == 200) {
return JSON.parse(request.response)
}
}
const systemJSON = readJSON('https://zkillboard.com/api/stats/solarSystemID/31000007/')
const printJSON = () =>{
console.log(systemJSON)
}
printJSON();
First off, when handling json from an external source I would suggest wrapping it in a try/catch function, to avoid unsuspecting errors.
Secondly I think the issue is that readJSON returns a promise, so you might need to await it.
try {
const json = await readJSON('https://zkillboard.com/api/stats/solarSystemID/31000007/')
const systemJSON = JSON.parse(json);
} catch (error) {
// Woops something happend - see error variable
}
I am using NodeJS request module to pass a JSON request to a URL and generate a JSON response from it. I tried this code and it generates a valid response. I am pasting the link for a StackOverflow question I asked for the same.
NodeJS Request returning an empty array inside a JSON response
However, when I utilize the same logic in AWS Lambda, there is no response at all from the module. Since there is no response at all, I cannot understand what the problem is.
This is the handling function for the AWS Lambda with Alexa as a trigger.
'use strict';
var request = require('request');
var accountNumberRequest = {};
var balanceResponse = {};
const url = "https://ibluatapig.indusind.com/app/uat/balinq/AccountEnquiry?client_id=6867b781-9b21-45c5-9c55-948f7cd1a33f&client_secret=hP3yB3hM2oH4pH4hM1kV3uY8vR3qV7jY8cF6bG2sF5jX8lT1vN";
var bal = {};
exports.handler = function (event,context) {
try{
console.log("Try Started");
var req = event.request;
console.log("Request Generated");
if(req.type === "LaunchRequest") {
console.log("Launch Request! Calling handleLaunchRequest");
handleLaunchRequest(context);
} else if(req.type === "IntentRequest") {
console.log("IntentRequest");
let options = {};
console.log(0);
if(req.intent.name === "BalanceIntent") {
console.log("Balance Intent");
//Got the account number from Alexa request
let accNo = req.intent.slots.AccountNumber.value;
console.log(accNo);
accountNumberRequest = {
"AERequest":{
"serviceType":"BE",
"deviceId":"Test",
"accountId":accNo
}
};
console.log(accountNumberRequest);
console.log("Calling NodeJS.Request");
request({
url: url,
method: "POST",
json: true,
header: {
"content-type": "application/json",
},
body: accountNumberRequest
},
function(error, response,body){
if(!error && response.statusCode === 200){
console.log(body.AEResponse.AcctBal[1].BalAmt);
} else {
//options.speechText = `The account <say-as interepret-as = "digits">${accNo}</say-as> does not exist`;
console.log("error: "+error);
console.log("response.statusCode"+response.statusCode);
console.log("response.statusText"+response.statusText);
}
}
);
console.log("Balance Response should be assigned by now");
console.log(bal);
/* if(accountNumbers.hasOwnProperty(accNo)) {
var balance = accountNumbers[accNo];
accountExists = true;
}
if(accountExists == true){
options.speechText = `The balance of account number <say-as interpret-as = "digits">${accNo}</say-as> is <say-as interpret-as = "cardinal">${balance}</say-as>`;
} else {
options.speechText = `The account <say-as interepret-as = "digits">${accNo}</say-as> does not exist`;
}*/
context.succeed(buildResponse(options));
}
} else if(req.type === "SessionEndedRequest") {
//Code here
} else {
throw("Unknown Intent Type");
}
} catch(e){
context.fail("Exception "+e);
}
};
function getBalance(){
//Code to parse the JSON response and extract values from the response.
}
function handleLaunchRequest(context){
//Code for handling launch requests }
function buildResponse(options){
//Code for generating response
}
This is the problem...
// You're sending an asynchronous HTTP request here.
request();
// But you sent the response here without waiting for the above request to finish.
context.succeed();
Basically, you're executing context.succeed() before request() finishes. So you're basically ending your Lambda invocation without the response from that HTTP request.
To fix your code, put the context.succeed() inside the callback that you pass to the request() call.
P.S.
You should be using callback instead of the deprecated context.succeed()/context.fail() API.
I am creating a website that reads externally hosted json files and then uses node.js to populate the sites content.
Just to demonstrate what I'm after, this is a really simplified version of what I'm trying to do in node.js
var ids = [111, 222, 333];
ids.forEach(function(id){
var json = getJSONsomehow('http://www.website.com/'+id+'.json');
buildPageContent(json);
});
Is what I want to do possible?
(Marked as a duplicate of "How do I return the response from an asynchronous call?" see my comment below for my rebuttal)
You are trying to get it synchronously. What you should aim for instead, is not a function used like this:
var json = getJSONsomehow('http://www.website.com/'+id+'.json');
but more like this:
getJSONsomehow('http://www.website.com/'+id+'.json', function (err, json) {
if (err) {
// error
} else {
// your json can be used here
}
});
or like this:
getJSONsomehow('http://www.website.com/'+id+'.json')
.then(function (json) {
// you can use your json here
})
.catch(function (err) {
// error
});
You can use the request module to get your data with something like this:
var request = require('request');
var url = 'http://www.website.com/'+id+'.json';
request.get({url: url, json: true}, (err, res, data) => {
if (err) {
// handle error
} else if (res.statusCode === 200) {
// you can use data here - already parsed as json
} else {
// response other than 200 OK
}
});
For a working example see this answer.
For more info see: https://www.npmjs.com/package/request
I think problem is in async request. Function will return result before request finished.
AJAX_req.open( "GET", url, true );
Third parameter specified async request.
You should add handler and do all you want after request finished.
For example:
function AJAX_JSON_Req( url ) {
var AJAX_req = new XMLHttpRequest.XMLHttpRequest();
AJAX_req.open( "GET", url, true );
AJAX_req.setRequestHeader("Content-type", "application/json");
AJAX_req.onreadystatechange = function() {
if (AJAX_req.readyState == 4 && AJAX_req.status == 200) {
console.log(AJAX_req.responseText);
}
};
}
I want to send file and json data from HttpClient to web api server.
I cant seem to access the json in the server via the payload, only as a json var.
public class RegulationFilesController : BaseApiController
{
public void PostFile(RegulationFileDto dto)
{
//the dto is null here
}
}
here is the client:
using (var client = new HttpClient())
{
using (var content = new MultipartFormDataContent())
{
client.BaseAddress = new Uri(ConfigurationManager.AppSettings["ApiHost"]);
content.Add(new StreamContent(File.OpenRead(#"C:\\Chair.png")), "Chair", "Chair.png");
var parameters = new RegulationFileDto
{
ExternalAccountId = "1234",
};
JavaScriptSerializer serializer = new JavaScriptSerializer();
content.Add(new StringContent(serializer.Serialize(parameters), Encoding.UTF8, "application/json"));
var resTask = client.PostAsync("api/RegulationFiles", content); //?ApiKey=24Option_key
resTask.Wait();
resTask.ContinueWith(async responseTask =>
{
var res = await responseTask.Result.Content.ReadAsStringAsync();
}
);
}
}
this example will work:HttpClient Multipart Form Post in C#
but only via the form-data and not payload.
Can you please suggest how to access the file and the submitted json And the file at the same request?
Thanks
I have tried many different ways to submit both file data and metadata and this is the best approach I have found:
Don't use MultipartFormDataContent, use only StreamContent for the file data. This way you can stream the file upload so you don't take up too much RAM on the server. MultipartFormDataContent requires you to load the entire request into memory and then save the files to a local storage somewhere. By streaming, you also have the benefit of copying the stream into other locations such as an Azure storage container.
This solves the issue of the binary data, and now for the metadata. For this, use a custom header and serialize your JSON into that. Your controller can read the custom header and deserialize it as your metadata dto. There is a size limit to headers, see here (8-16KB), which is a large amount of data. If you need more space, you could do two separate requests, one to POST the minimum need, and then a PATCH to update any properties that needed more than a header could fit.
Sample code:
public class RegulationFilesController : BaseApiController
{
public async Task<IHttpActionResult> Post()
{
var isMultipart = this.Request.Content.IsMimeMultipartContent();
if (isMultipart)
{
return this.BadRequest("Only binary uploads are accepted.");
}
var headerDto = this.GetJsonDataHeader<RegulationFileDto>();
if(headerDto == null)
{
return this.BadRequest("Missing X-JsonData header.");
}
using (var stream = await this.Request.Content.ReadAsStreamAsync())
{
if (stream == null || stream.Length == 0)
{
return this.BadRequest("Invalid binary data.");
}
//save stream to disk or copy to another stream
var model = new RegulationFile(headerDto);
//save your model to the database
var dto = new RegulationFileDto(model);
var uri = new Uri("NEW URI HERE");
return this.Created(uri, dto);
}
}
private T GetJsonDataHeader<T>()
{
IEnumerable<string> headerCollection;
if (!this.Request.Headers.TryGetValues("X-JsonData", out headerCollection))
{
return default(T);
}
var headerItems = headerCollection.ToList();
if (headerItems.Count() != 1)
{
return default(T);
}
var meta = headerItems.FirstOrDefault();
return !string.IsNullOrWhiteSpace(meta) ? JsonConvert.DeserializeObject<T>(meta) : default(T);
}
}
Anyone knows how to send the request using JSON content in windowsphone. I had the JSON parameters how to post it.
Simply serialize the data in JSON, and write it as a POST request to the server. Here's how I do it in one of my apps:
private static IObservable<T> GetDataAsync<T, TRequest>(TRequest input, string address)
{
var request = HttpWebRequest.Create(address);
request.Method = "POST";
var getRequestStream = Observable.FromAsyncPattern<Stream>(
request.BeginGetRequestStream,
request.EndGetRequestStream);
var getResponse = Observable.FromAsyncPattern<WebResponse>(
request.BeginGetResponse,
request.EndGetResponse);
return getRequestStream()
.SelectMany(stream =>
{
try
{
using (var writer = new StreamWriter(stream))
writer.WriteLine(JsonConvert.SerializeObject(input));
}
catch
{
// Intentionally ignored.
}
return getResponse();
})
.Select(webResponse =>
{
using (var reader = new StreamReader(webResponse.GetResponseStream()))
return JsonConvert.DeserializeObject<T>(reader.ReadToEnd());
});
}