Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 208
SeaFuzz Avatar asked May 04 '18 19:05

SeaFuzz


1 Answers

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();
like image 114
SeaFuzz Avatar answered Nov 15 '22 00:11

SeaFuzz