Dart json.encode is not encoding as needed by Firebase Function - json

I've been working on this issue for some time now, and I can't seem to figure out what exactly the issue is. In Dart(2) the json.encode() does not seem to be giving me the result I am aiming for.
I am passing in a Map<String, dynamic> that looks like this:
_data = <String, dynamic>{
'uid': newProfileCreationModel.accountID,
'displayName': newProfileCreationModel.profileName,
'greeting': newProfileCreationModel.profileBody ?? '',
'avatarSrc': newProfileCreationModel.avatarSrc,
'heroSrc': newProfileCreationModel.heroSrc,
'accountType': newProfileCreationModel.accountType,
'cityID': newProfileCreationModel.cityID,
'cityName': newProfileCreationModel.cityName,
'country': newProfileCreationModel.countryCode,
'state': newProfileCreationModel.stateAbv,
};
and using this to convert it to JSON
final String _jsonData = json.encode(_data);
Then I am sending it to a google cloud function in the following way.
final Uri _uri = Uri.parse(functionUrl);
final String _jsonData = json.encode(_data);
final String _userToken = await AuthenticationService.getUserToken();
HttpClient client = new HttpClient();
HttpClientRequest request = await client.postUrl(_uri);
request.headers.set('Authorization', 'Bearer ' + _userToken);
request.headers.set('Content-Type', 'application/json; charset=utf-8');
print('Encoded: ' + _jsonData);
request.write(_jsonData);
HttpClientResponse response = await request.close();
if(response.statusCode != HttpStatus.OK){ ...
The line where I print the encoded string outputs this to the console:
05-04 18:52:57.902 26146-26200/com.app.name I/flutter: Encoded: {"uid":'123456789',"displayName":"James","greeting":"My Greetings!","avatarSrc":"http://cdn.free.com/someImage.jpg","heroSrc":"http://cdn.free.com/someImage.jpg","accountType":"per","cityID":1,"cityName":"Eugene","country":"US","state":"OR"}
However the request.write(_jsonData) fails with the following firebase console log error Request Body Missing Data the response that looks like this in the
Firebase Console Log.
Request body is missing data. {
uid: '123456789',
displayName: 'James',
greeting: 'My Greetings!',
avatarSrc: 'http://cdn.free.com/someImage.jpg',
heroSrc: 'http://cdn.free.com/someImage.jpg', accountType: 'per',
cityID: 1,
cityName: 'Eugene',
country: 'US',
state: 'OR'
}
Invalid request IncomingMessage {
_readableState:
ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: null,
pipesCount: 0,
flowing: true,
ended: true,
endEmitted: true,
reading: false,
sync: false,
needReadable: false,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
defaultEncoding: 'utf8',
ranOut: false,
awaitDrain: 0,
readingMore: false,
decoder: null,
encoding: null },
readable: false,
domain: null,
_events: {},
_eventsCount: 0,
_maxListeners: undefined,
socket:
Socket {
connecting: false,
_hadError: false,
_handle:
TCP {
bytesRead: 13285,
_externalStream: {},
fd: 14,
reading: true,
owner: [Circular],
onread: [Function: onread],
onconnection: null,
writeQueueSize: 0,
_consumed: true },
_parent: null,
_host: null,
The interesting part is that the data is getting through as is clearly displayed in the firebase console log, however it won't recognize it as the body.
Raw Data Approach
When I try sending a raw JSON object through the request.write() as
request.write({'hello':'universe'});
I get a totally different kind of error in the firebase console.
SyntaxError: Unexpected token h in JSON at position 1
at Object.parse (native)
at parse (/var/tmp/worker/node_modules/body-parser/lib/types/json.js:84:17)
at /var/tmp/worker/node_modules/body-parser/lib/read.js:102:18
at IncomingMessage.onEnd (/var/tmp/worker/node_modules/raw-body/index.js:149:7)
at IncomingMessage.g (events.js:292:16)
at emitNone (events.js:86:13)
at IncomingMessage.emit (events.js:185:7)
at endReadableNT (_stream_readable.js:974:12)
at _combinedTickCallback (internal/process/next_tick.js:80:11)
at process._tickDomainCallback (internal/process/next_tick.js:128:9)
on the firebase side I am using a function using a callable, this is where the firebase console logs are being recorded from
export const finalizeProfile = functions.https.onCall((data, context) => {
//CODE
});
Is anyone able to spot what I may be doing incorrectly?

The solution was hidden in plain sight. The issue had to do with a missing field. for Firebase Cloud Functions, the contents 'body', as referred to in the error message body is missing data, literally means it requires a key named data which contains the object of data you are passing.
The google docs are not(ish) crystal clear on this, as they are missing an example https://firebase.google.com/docs/functions/callable-reference#request_body
This is how the data must be sent to functions.https.onCall(), note the Data Field as compared to the original question which did not include this:
{
"data":{
"uid":"123456789",
"displayName":"James",
"greeting":"My Greeting!",
"avatarSrc":"http://cdn.free.com/someImage.jpg",
"heroSrc":"http://cdn.free.com/someImage.jpg",
"accountType":"per",
"cityID":1,
"cityName":"Eugene",
"country":"OR",
"state":"AL"
}
}
The resulting code that now works looks as follows:
// Helper function to format any map to be used for Firebase
Map prepMapForFirebaseJSON(Map<String, dynamic> mapData){
Map<String, Map<String, dynamic>> _fireJson = {
'data' : mapData
};
return _fireJson;
}
// Process HTTP request
final Uri _uri = Uri.parse(functionUrl);
final String _jsonData = json.encode(prepMapForFirebaseJSON(mapData));
final String _userToken = await AuthenticationService.getUserToken();
HttpClient client = new HttpClient();
HttpClientRequest request = await client.postUrl(_uri);
request.headers.set('Authorization', 'Bearer ' + _userToken);
request.headers.set('Content-Type', 'application/json; charset=utf-8');
request.write(_jsonData);
request.close();

I think closing the request is missing:
request.write(_jsonData);
var response = await request.close();

Related

Parse FormData object in nest controller

I have a nest controller which accepts the following DTO, One of the parameters should be a buffer which is read from file
It suppose to read bin file
interface LoadFileRequest {
targetIp: string;
fileBuffer: Buffer;
user: User;
}
interface User {
username: string;
password: string;
}
#Controller('loader')
export class KeyLoaderController {
#Post('load')
async load(#Body() body: LoadFileRequest, #Req() request) {
console.log(body);
console.log(request);
}
}
My code for testing the controller:
import axios from "axios";
import fs from "fs";
import FormData from "form-data";
let headersList = {
Accept: "*/*",
};
let formdata = new FormData();
formdata.append("targetIP", "10.10.1.145");
formdata.append(
"user",
JSON.stringify({
username: "user",
password: "auth",
})
);
formdata.append("fileBuffer", fs.createReadStream("test.bin"));
let bodyContent = formdata;
const url = "http://localhost:3000/loader/load";
let reqOptions = {
url,
method: "POST",
headers: headersList,
data: { bodyContent },
};
axios.request(reqOptions).then(function (response) {
console.log(response.data);
});
At the controller this is the request body i get
{
bodyContent: {
_overheadLength: 380,
_valueLength: 48,
_valuesToMeasure: [ [Object] ],
writable: false,
readable: true,
dataSize: 0,
maxDataSize: 2097152,
pauseStreams: true,
_released: false,
_streams: [
'----------------------------092840744027446269037569\r\n' +
'Content-Disposition: form-data; name="targetIP"\r\n' +
'\r\n',
'10.10.1.145',
null,
'----------------------------092840744027446269037569\r\n' +
'Content-Disposition: form-data; name="user"\r\n' +
'\r\n',
'{"username":"user","password":"auth"}',
null,
'----------------------------092840744027446269037569\r\n' +
'Content-Disposition: form-data; name="fileBuffer"; filename="test.bin"\r\n' +
'Content-Type: application/octet-stream\r\n' +
'\r\n',
[Object],
null
],
_currentStream: null,
_insideLoop: false,
_pendingNext: false,
_boundary: '--------------------------092840744027446269037569'
}
}
How can i get the body in the form of object with only the attrbutes of the DTO?
Eventually i got a method which recived the same type of object and i want to call it like this:
#Controller('loader')
export class KeyLoaderController {
#Post('load')
async load(#Body() body: LoadFileRequest, #Req() request) {
return await loadFile(body);
}
}
EDIT:
I changed the code a bit and now if i send the request from VS code thunder client it works fine(sort of):
#Controller('loader')
export class KeyLoaderController {
#Post('load')
#UseInterceptors(FileInterceptor('fileBuffer'))
async load(#Body() body, #UploadedFile() file) {
console.log(body);
console.log(file);
}
}
When I send this way (The code on the right is the generated code by the extension, Which does not act the same way when I use it):
This is the body and file I get:
[Object: null prototype] {
targetIP: '10.10.1.145',
user: '{ "username": "user", "password": "0123456789"}'
}
{
fieldname: 'fileBuffer',
originalname: 'test.bin',
encoding: '7bit',
mimetype: 'application/octet-stream',
buffer: <Buffer ... ... 119 more bytes>, size: 169
}
But when i take the code i generated in VS code thunder client (which is at axios request in this post) I still get the body like before (again, as mention in the post)
If you're sending a multipart/form-data request, you need a form data body parser installed on the server. You can either use the FileInterceptor (or one of the derivatives of it), or bind multer as a middleware for the entire server. This will allow for the parsing of multipart/form-data requests.
In your axios call, remove the brackets around bodyContent so it becomes data: bodyContent. You aren't sending an object of form data you are just sending formdata itself

Spring returns gibberish to JS client

Here is the response I get from my WebSocket controller whenever it publishes a message to it's subscribers
n {command: "MESSAGE", headers: {…}, body: "{\"headers\":{},\"body\":{\"sender\":\"b\",\"recipient\":\"my…dy\":\"a\"},\"statusCode\":\"OK\",\"statusCodeValue\":200}", escapeHeaderValues: true, ack: ƒ, …}
ack: ƒ (e)
body: "{\"headers\":{},\"body\":{\"sender\":\"b\",\"recipient\":\"myself\",\"body\":\"a\"},\"statusCode\":\"OK\",\"statusCodeValue\":200}"
command: "MESSAGE"
escapeHeaderValues: true
headers: {content-length: "108", message-id: "ec3f952a-e9f6-2d4e-997b-07bd18bd5d58-14", subscription: "sub-0", content-type: "application/json", destination: "/topic/greetings"}
Everything gets garbled into one ugly string under "body", even though when I log the messages in my server, I see a proper JSON object.
Here is my controller
public class WebsocketController {
private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketController.class) ;
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public ResponseEntity<MyMessage> webSocketMessage(MyMessage message) {
LOGGER.info("Msg is : " + message);
return new ResponseEntity<>(message, HttpStatus.OK);
}
}
And my JS client
function connect() {
var socket = new WebSocket('ws://localhost:8081/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', (messageOutput) => {
console.log(messageOutput);
showMessageOutput(JSON.parse(messageOutput.body));
});
});
}
Solved the problem, but still don't understand some parts:
My response is already a JSON. However, the body is a string NOT in JSON form, so I wrote this to fix it:
console.log(JSON.parse(messageOutput.body).body);
It's a solution, but it smells. If someone could shed some light on why the body of the response came as a string and not as a JSON it would be great.

Send multiple files in json body

I am new in flutter, trying to send multiple files in single request but failing miserably. I have attached the json body parameter screenshot also.
I have to send an array of multiple pdf files along with other parameters.
qualifications:{"qualifications": [{"id": "25", "qualification": "nurse", "grade": 11, "date": "2020-07-20", "proof": "file"}]}
nurse_id:26
This is what i did
List<Map> _jsonArray = new List();
Map<String, String> arrayBody = {
'id': '',
'qualification': 'nurse',
'grade': '13',
'date': _dateController.text,
};
List<http.MultipartFile> _mfiles = new List();
_jsonArray.add(arrayBody);
var body = json.encode({'qualifications':_jsonArray });
_mfiles.add( await http.MultipartFile.fromPath(
'proof',
_file
),
);
_mfiles.add( await http.MultipartFile.fromPath(
'qt_statement',
_qtStatement
),);
final Map<String, String> requestheaders = {
'Authorization': 'Bearer '+token_id,
};
var request = http.MultipartRequest('POST', Uri.parse(Urls.PERSONAL_DETAILS));
request.headers.addAll(requestheaders);
request.files.addAll(_mfiles);
request.fields[Constants.nurse_id] = nurse_id;
var res = await request.send();
var content = StringBuffer();
if(res.statusCode == 200){
res.stream.transform(utf8.decoder).listen((value) {
content.write(value);
},
onDone: (){
var data = json.decode(content.toString());
print(data);
});
}
The json i am sending looks like this
{"qualifications":[{"id":"","qualification":"nurse","grade":"13","date":"2020-07-31"}]}
Here 'proof' key is missing. Hence, getting 'Proof cannot be null' as response from server.
Struggling with this issue for more than 3 hours. Any suggestion or help will be highly appreciated
i assume your server expects the request to have a field called proof which contains the file data as part of the json post data. In this case, you can encode the file content (bytes) as base64 and set it inside your json data.
something like:
string proof = read_file_as_base64(filepath); //you need to read and encode the file content here.
Map<String, String> arrayBody = {
'id': '',
'qualification': 'nurse',
'grade': '13',
'date': _dateController.text,
'proof': proof
};

how to post json array on flutter

I have some problem to post json array with flutter.
When I hit api with postman using json it works. Screenshot postman:
Since I know on body just accept Map <String,String> CMIIW
so i turn body into like this
List<Map<String,String>> products = [
{
"product_id": "1",
"buy_quantity": "1",
"product_price": "1000",
"is_voucher": "0",
},
{
"product_id": "2",
"buy_quantity": "2",
"product_price": "2000",
"is_voucher": "0",
},
];
final String jsonProduct = json.encode(products);// here im trying to
Map<String,String> _body = {
"buyer_id": '',
"buyer_firstname": postCart.buyerFirstname,
"phone_number": postCart.phoneNumber,
"transaction_total_price": postCart.transactionTotalPrice.toString(),
"voucher_id": 0.toString(),
"voucher_code": 0.toString(),
"payment_id": postCart.paymentId.toString(),
"payment_name": postCart.paymentName,
"products" : jsonProduct
};
but i still got error,
thanks!
I'm assuming that you are using the http package.
This is an example of how to make an HTTP POST request with a json payload on the body:
Future<Lead> createLead(String clientName, String clientEmail, String clientPhone, String eventId) async {
// Create storage
final storage = new FlutterSecureStorage();
// Get API url from env
String url = (DotEnv().env['BASE_URL'] + "/leads/create");
String authToken = await storage.read(key: 'api-token');
// Create some request headers
Map<String, String> requestHeaders = {
'Content-type': 'application/json',
'Accept': 'application/json',
'X-Token': authToken
};
final response = await http.post(
url,
// enconde some JSON data on the request body
body: json.encode(
{
'event_id': eventId,
'name': clientName,
'phone': clientPhone,
'email': clientEmail
}
),
headers: requestHeaders
);
if (response.statusCode == 200) {
final leadsJson = json.decode(response.body);
Lead lead = Lead.fromJson(leadsJson);
return lead;
} else {
// If that response was not OK, throw an error.
// throw Exception('Failed to load post');
return null;
}
}
Hope it helps.
If you want to achieve the JSON in the screen shot you need to make several changes.
Notice that the list of products shows unquoted integers when converted to JSON, so these need to be kept as ints in your Dart class. (Note the preferred syntax, too.)
var products = <Map<String, int>>[
{
'product_id': 1,
'buy_quantity': 1,
'product_price': 1000,
'is_voucher': 0,
},
{
'product_id': 2,
'buy_quantity': 2,
'product_price': 2000,
'is_voucher': 0,
},
];
The type of _body needs to change because it now contains a list as well as strings. (Keep the rest the same - like the toStrings - as your screenshot shows that even the integers are quoted as strings.)
var _body = <String, dynamic>{
'buyer_id': '',
'buyer_firstname': postCart.buyerFirstname,
'phone_number': postCart.phoneNumber,
'transaction_total_price': postCart.transactionTotalPrice.toString(),
'voucher_id': 0.toString(),
'voucher_code': 0.toString(),
'payment_id': postCart.paymentId.toString(),
'payment_name': postCart.paymentName,
'products': products
};
Lastly json and (preferably) utf8 encode the resulting map, and pass that as the body parameter of your http.post.
var bytes = utf8.encode(json.encode(_body));
await http.post(url, body: bytes, headers: someHeaders);
One of your headers could be content-type: application/json;encoding=utf-8 to tell the server that you are sending UTF8 encoded JSON.

HUBOT Coffescript - JSON obejct

Wondering if anyone can help me with this - pretty much novice in scripting and not sure if I'm doing it right. I need to get an object from this JSON string and it keeps giving me "undefined" error.
Here's the JSON:
`
{ data:
[ { type: 'gif',
id: 'Cmr1OMJ2FN0B2',
slug: 'hello-Cmr1OMJ2FN0B2',
url: 'https://giphy.com/gifs/hello-Cmr1OMJ2FN0B2',
bitly_gif_url: 'https://gph.is/2bZufS7',
bitly_url: 'https://gph.is/2bZufS7',
embed_url: 'https://giphy.com/embed/Cmr1OMJ2FN0B2',
username: '',
source: 'https://www.fanpop.com/clubs/penguins-of-madagascar/images/37800672/title/hello-photo',
rating: 'g',
content_url: '',
source_tld: 'www.fanpop.com',
source_post_url: 'https://www.fanpop.com/clubs/penguins-of-madagascar/images/37800672/title/hello-photo',
is_indexable: 0,
import_datetime: '2016-09-05 13:48:36',
trending_datetime: '2017-09-19 14:26:18',
images: [Object],
title: 'bom dia hello GIF' } ],
pagination: { total_count: 2516, count: 1, offset: 0 },
meta:
{ status: 200,
msg: 'OK',
response_id: '5a28576867382f644dc7d33b' } }
`
And here's my HUBOT script:
`
robot.hear /^(no)$|^.*(\sno\s).*$/i, (res) ->
api_url = 'https://api.giphy.com'
path = '/v1/gifs/search'
url = "#{api_url}#{path}"
robot.http(url)
.query
q: "nono+penguin"
rating: 'g'
limit: 1
fmt: 'json'
.header('api_key', giphyAuthToken)
.header('Content-Type', 'application/json')
.get() (err, res, body) ->
# error checking code here
if err
console.log err
else
data = JSON.parse(body)
console.log data #this prints above data
console.log "success....got giphy response"
console.log data.images.original.url #This is giving error that original is undefined
process.exit(1)
`
Wondering how can I access this "images" object from Giphy's response.
Thanks
The data field in your object is an Array so you need to put the index in order to access the content, i.e.
data = JSON.parse(body)
console.log data[0].images