POST action with XML payload


#1

Hi!

I’m trying to send to the server through adapter the record ‘report’. This is my adapter:

import DS from 'ember-data';
import RSVP from 'rsvp';
const { Promise } = RSVP;

export default DS.Adapter.extend({

  defaultSerializer: 'report',

  findAll(store){
    return new Promise(function(resolve, reject) {
      let report = store.peekAll('report');
      resolve(report);
    })
  },

  createRecord(store, type, snapshot){

      let data = this.serialize(snapshot, { includeId: true });
      return new Promise(function(resolve, reject){
        let xhr = new XMLHttpRequest();
        var method = 'POST';
        var url = 'https://test.vigifarmaco.it/api/v1/submit';

        if ("withCredentials" in xhr) {
        // XHR for Chrome/Firefox/Opera/Safari.
          xhr.open(method, url, true);
        } else if (typeof XDomainRequest != "undefined") {
          // XDomainRequest for IE.
          xhr = new XDomainRequest();
          xhr.open(method, url);
        } else {
          // CORS not supported.
          xhr = null;
        }

        if (!xhr) {
          alert('CORS not supported');
          return;
        }


        xhr.onreadystatechange = alertContents;
        xhr.setRequestHeader("Vigifarmaco-Api-Key", "0Utrr0yHwHQbXr9TT1COrQtt");
        xhr.setRequestHeader("Content-type" , "text/xml");
        xhr.send(data);


        function alertContents() {
          if (this.readyState == 4){
            if(this.status == 200){

              resolve(xhr.responseXML);
            }
            else
              reject(new Error('post: `' + url + '` failed with status: [' + this.status + ']'+ 'failed with readyState:' + this.readyState+' error:' + xhr.responseXML.getElementsByTagName('errors')[0].children[0].firstChild.nodeValue));

          }
        }

this is the controller where i save the record:

import Ember from 'ember';

const {
  Controller
 } = Ember;

export default Controller.extend({
  actions:{
    submit(){
      this.get('model').reports.save().then(function(value){
        alert(value.data.id);
        alert('segnalazione inviata');
        alert(value);
        this.transitionToRoute('report.lastpage');
        //return value;
      }, function(error){
        alert('errore');
        console.log(error);
      });

      //get(this, 'model').destroyRecord();

    }
  }


});

My problem is that when i try to save the record i get the following message: “Assertion Failed: ‘motorista’ was saved to the server, but the response does not have an id and your record does not either.”

The server response with an XML document.

Any suggestion?


#2

Hi @emiliogambone, I think the basic issues is that Ember Data either isn’t receiving an id in the returned XML, or doesn’t know how to extract the id properly. Typically Ember Data would expect the response object (whether JSON or XML or whatever) to have a full record with any relevant attributes and an id attribute.

So… if the XML response already does have an id, the problem seems to be that Ember Data is not sure where to find the ID, or rather it’s not being normalized properly. My suggestion in this case would be to use the “normalize” method in your serializer to extract the id from the xml and set it on the “record hash” properly. Essentially you’re telling Ember Data “hey, there is an id on this document and this is how to extract it”

If the XML response does NOT contain an id, you have two options:

  1. modify the backend code to return an id
  2. synthesize your own id in the serializer layer. We do this in our apps with a couple of our models because our server doesn’t return ids, so in the serializer normalize method we make one up as a sort of “composite key” made up several other attributes. If you do this you’ll probably need to remove the id field in the serializer ‘serialize’ method so it doesn’t send it to the backend

#3

I don’t have any id in the XML response. Ember data would expect the response object with only the id or a full record?

In my serializer, I essentialy parse from JSON to XML for save the record. But i just find out that on the record that i’m trying to save my id is null.


#4

I think by default Ember Data would expect a full object in the “create” aka POST response, and that would include an id. Essentially, you create a new record in the front-end, load it up with whatever attributes you want, do a record.save() which sends a POST to the backend (without an id), and then the backend takes that record, does all the usual backend CRUD stuff, and then returns the full record with a 200 response code (or maybe 201 or something? I forget right offhand) and includes an id. That id of course represents the id that the backend has assigned that record. So if it was… say… an inventory management system and you created a new ‘item’ record and saved it to the backend, the backend would say “Oh i see that you are trying to create a new item”, save it to the database (thereby creating a unique id for that item), and return the “saved” item with that id.

Of course sometimes the backend doesn’t create an id, or at least doesn’t return that id with the record. In that case you’d want to synthesize one in the “normalize” method of your serializer, typically using a compound key constructed from other attributes, or I guess you could just make it random.


#5

Thank you! I can explain it better, i don’t know why but, the record that i want to send to the back-end does not have any id. The id field is null. Should I fill it in some way? Or i only need to normalize it when I have the POST response? In my case the back-end does not return any id, i have to create it.


#6

Is the record you are sending to the backend being created on the front-end? If so I think that’s pretty normal. I believe if you do store.create('model-name', ...) Ember creates it without an ID but the server typically provides an ID in the response.

If the backend does not provide an ID I think you’ll want to artificially create one in the serializer (Ember Data requires an ID for records). This should be done in the normalize method. Then in the serialize method you’ll probably want to strip the id back out.

The data flow and serialize layer look like this:

           |--  serializer --|
Backend => |    normalize    | => Ember data record (triggered by findAll, findRecord, query, etc.)
Backend <= |    serialize    | <= Ember data record (triggered by record.save(), etc.)

The normalize and serialize methods are called by a bunch of other methods like normalizeArrayResponse, etc. so you can customize behavior only for specific types of responses as well.