POSTing data : not working on server with HTTP / working on client with jQuery

Hello everybody,

For some days now, I am facing a problem while making a request with POST data, and I have to admit I clearly do not understand what is going on. Let me explain in details.

For my project I have to use the API provided by i.materialise.

I am already using some of their API like “Materials Catalog”, “Upload Model”, “Pricing by Model ID”. Any problem for these.

Now I have to use the “Cart Item Creation API” but I cannot get it working on server side. I always get the error :
"error":{"message":"Wrong request body. Check if all parameters set correctly","code":401},"cartItems":[]}

I am not doing something complicated, just a simple Meteor.call() from the client, and the method on the server is very simple :

On client

Meteor.startup(function () {
  Meteor.call( 'imaterialise.cartItemCreate' );
});

On server

Meteor.methods({
  'imaterialise.cartItemCreate': function imatCartItemCreate() {
    var endpoint = 'https://imatsandbox.materialise.net/web-api/cartitems/register';

    var dataToSend = {
      "cartItems": [{
        "toolID"             : "<MY-TOOL-ID>",
        "MyCartItemReference": "POKpojkdza",
        "modelID"            : "<MY-MODEL-ID>",
        "fileUnits"          : "mm",
        "fileScaleFactor"    : "1",
        "materialID"         : "6d65e2e0-6eee-4df4-951d-e5db38bd1660",
        "finishID"           : "2f01fc24-f2df-4e3e-abd3-a5408fe6f836",
        "quantity"           : "1",
        "xDimMm"             : "1",
        "yDimMm"             : "1",
        "zDimMm"             : "1",
        "volumeCm3"          : "0.11",
        "surfaceCm2"         : "1",
        "iMatAPIPrice"       : "0",
        "mySalesPrice"       : "0"
      }],
      "currency": "GBP"
    };

    HTTP.call( 'POST', endpoint, {

      headers: {
        'Accept': 'application/json',
        // 'Content-Type' : 'multipart/form-data',
      },

      data: dataToSend,

    }, function (error , response) {

      console.log( 'imaterialise.cartItemCreate()', error, response );

    });
  },
});

Of course I tried to put the variable dataToSend everywhere, in params, in content, in query, stringified or not, …

However, what is really strange is that if I try to make this POST request with jQuery it works perfectly. (see this JSFiddle) (I replaced sensitive data like the toolID and the modelID, but trust me it works :slight_smile: )
If I copy this code into a file on client-side (example client/main.js) it works perfectly. (whereas I was expecting the opposite, as in general it is more recommended to make HTTP requests on server side, no?)

The data I send is correct (as we can see on the JSFiddle), the HTTP call is very simple, and even if the project I am currently working on uses a very old version of Meteor (1.1.0.2 …) I tried with a blank project with version 1.6.1 and I get the same error … I am completely lost.

Am I doing something wrong with this simple POST request ?
Do you think it could be related to the server providing the API ?

I hope I was clear enough and did not forget to mention any useful information, if not do not hesitate.

Thanks a lot !

If the API is expecting form data (as for example, most PHP applications), then what usually works for me would look like this in your method:

const dataToSend = {
  cartItems: [{
    toolID: '<MY-TOOL-ID>',
    MyCartItemReference: 'POKpojkdza',
    modelID: '<MY-MODEL-ID>',
    fileUnits: 'mm',
    fileScaleFactor: '1',
    materialID: '6d65e2e0-6eee-4df4-951d-e5db38bd1660',
    finishID: '2f01fc24-f2df-4e3e-abd3-a5408fe6f836',
    quantity: '1',
    xDimMm: '1',
    yDimMm: '1',
    zDimMm: '1',
    volumeCm3: '0.11',
    surfaceCm2: '1',
    iMatAPIPrice: '0',
    mySalesPrice: '0'
  }],
  currency: 'GBP'
};
try {
  const result = HTTP.call('POST', endpoint, { params: dataToSend });
  // do something with result
} catch (error) {
  throw new Meteor.Error('EPOST', error.message);
}

I tested that locally with a small PHP application and it worked for me.

Note, that you don’t need to use the callback form on the server. It’s usually easier to use the sync-style form - especially if you want to work with MongoDB.

@robfallows, thank you for your answer.

Unfortunately, it is still not working. (I even tried to change the code, with you sync-style, in my blank project with Meteor 1.6.1) I keep getting the same error as before.

{ error: {
  message: 'Wrong request body. Check if all parameters set correctly',
  code: 401
}, cartItems: [] }

According to the API documentation and the developer in charge of it, we may need to add the header Content-Type: multipart/form-data, but when I do this I get a new error :
Error invoking Method 'imaterialise.cartItemCreate': failed [500] Unknown Error. Try again later. If the problem persists, please contact us. [EPOST].

That is why I am missing something… Or maybe I was thinking that it is related with the remote server.

Client file (same as my first post)

Server file

  'imaterialise.cartItemCreate': function imatCartItemCreate() {

    var dataToSend = {
      "cartItems": [{
        "toolID": "<TOOL-ID>",
        "MyCartItemReference": "POKpojkdza",
        "modelID": "<MODEL-ID>",
        "fileUnits": "mm",
        "fileScaleFactor": "1",
        "materialID": "6d65e2e0-6eee-4df4-951d-e5db38bd1660",
        "finishID": "2f01fc24-f2df-4e3e-abd3-a5408fe6f836",
        "quantity": "1",
        "xDimMm": "1",
        "yDimMm": "1",
        "zDimMm": "1",
        "volumeCm3": "0.11",
        "surfaceCm2": "1",
        "iMatAPIPrice": "0",
        "mySalesPrice": "0"
      }],
      "currency": "GBP"
    };

    var endpoint = 'https://imatsandbox.materialise.net/web-api/cartitems/register';

    try {

      var result = HTTP.call('POST', endpoint, {
        headers: {
          Accept: 'application/json', // documentation ask for this

          // adding this header throws an error
          // 'Content-Type': 'multipart/form-data'
        },
        params: dataToSend // or 'data' key
      });

      console.log( 'Request finished', result.statusCode, result.data );

    } catch (error) {
      throw new Meteor.Error('EPOST', error.message);
    }
},

I don’t know why that should be. Your example looks ok to me - and I’ve used that header myself with no problems.

If no one else comes up with an answer, I’ll take a closer look myself tomorrow.

The header is, apparently, not used on the JSFiddle example. So I am not sure if I really need it.

Once again thank you for your help.

Well, I can confirm that setting Content-Type: multipart/form-data causes the remote server to respond with a 500 error. I’ve also confirmed that with PostMan, so that definitely seems to be at their end.

On the other hand, I can’t get their own example to work, even with PostMan and tweaking lots of different settings.

Currently stumped. Sorry.

@robfallows, glad to hear news from you. No good news from my side neither.

In order to help you (more) understanding the problem, would you like me to give you the tool ID and a model ID to make real requests ?

So I’m now convinced that the request must be made with Content-Type: multipart/form-data. I expect the jQuery example sets this header for you.

The provided content must then comply with the requirements for multipart.

You may have success using rosh93:multipart-form-data to encode the data before posting.

1 Like

I will try this package and give a feedback :slight_smile: .