javascriptjqueryajaxjquery-callback

ajax request gives 500 error when called from another ajax request


I have two ajax requests, one which sends data to the backend (Let's call it Ajax#1), and then one which uses that sent data to load stuff onto the front end (Ajax#2).

In order to make sure that Ajax#2 runs after Ajax#1, I tried passing the function containing #2 as a callback to the function containing #1, where it is called after #1 finishes.

However this always seems to produce a 500 error.

Ajax #2 runs fine on it's own (when triggered by a button press), but when called from Ajax #1 it always gives a 500 error.

Any idea what I am doing wrong here?

var initialize = function(){

  //Load settings config on page load (this works fine)
  load_settings_config();

  //Should apply settings on button press, and load those new settings to front end
  $("#apply-sysconfig-button").click(function(){
    apply_settings_config(load_settings_config);
  });

  //manual load settings button (This works fine)
  $("#load-settings-button").click(function(){
    load_settings_config();
  });

};

apply_settings_config = function(callback){

  //Not shown - Grabs user input from html input and select elements and loads into the variable 'settings' to be sent to back end
  //Ajax#1
  $.ajax({
      method: "POST",
      contentType: 'application/json',
      url: "/applynewsettings",
      data: JSON.stringify(settings)
  })
  .done(function(sysconfig_err_msg){
    //prompt user with error message if user input is invalid
    $("#static-ip-input-err").html(sysconfig_err_msg);

    //This does not work - gives 500 error
    callback()
  });
};

load_settings_config = function(){
  //loads the new/updated settings back to front end
  //Ajax#2
  $.ajax({
    method: "GET",
    contentType: 'application/json',
    url: "/loadinterfaceinfo"
  })
    .done(function(interface_info_new){
        interface_info = JSON.parse(interface_info_new);
        interface_selected = $("#interface-dropdown option:selected").text()
        populate_interface_info(interface_selected);

  });
};

Note that I also tried

 $("#apply-sysconfig-button").click(function(){
    apply_settings_config();
    load_settings_config();
  });

Which unsurprisingly causes load to often run before apply finishes.

And I tried calling the function directly from within ajax like this:

apply_settings_config = function(){

  //Not shown - Grabs user input from html input and select elements and loads into the variable 'settings' to be sent to back end
   //Ajax#1
  $.ajax({
      method: "POST",
      contentType: 'application/json',
      url: "/applynewsettings",
      data: JSON.stringify(settings)
  })
  .done(function(sysconfig_err_msg){
    //prompt user with error message if user input is invalid
    $("#static-ip-input-err").html(sysconfig_err_msg);

    //This does not work - gives 500 error
    load_settings_config()
  });
};

Which gives the same 500 error.

I seem to be missing some part of how to do callbacks with asynchronous get/post requests. Any help would be much appreciated


EDIT It was requested to see the backend. I'm not sure how that would help considering that both requests work just fine in most cases. It is only when the ajax requests are nested that the outer does not work, but here they are anyway:

@app.route("/applynewsettings", methods=['POST'])
def apply_new_settings():
    global sys_settings
    sia_err_msg = ""

    if request.method == 'POST':
        sys_settings = request.json

    sys_settings = convertToASCII(sys_settings)

    mtu = sys_settings["mtu"]
    ip_address = sys_settings["ip_address"]
    subnet_mask = sys_settings["subnet_mask"]
    gateway = sys_settings["gateway"]
    interface = sys_settings["interface"]

    #Not shown - validation and build string to send command

    if "sia" in sia_cmd:
        cli.sendCmd(sia_cmd)
        sia_err_msg = "Sucess!"
        print "SEND CMD: "+sia_cmd
    else:
        print "sia cmd was invalid: "+"'"+sia_cmd+"'"
        sia_err_msg = sia_err_msg + "Apply Settings Failed!"

    return sia_err_msg

#Sends cmd through telnet cli, parses response, returns to front end
@app.route("/loadinterfaceinfo")
def load_interface_info():
    #Sends cmd through telnet cli & parses response
    interface_dict = run_cmd(cli,"get_interface_info")
    #must encode as json to send to front end
    interface_dict = json.JSONEncoder().encode(interface_dict)
    #send to front end
    return interface_dict

Solution

  • The source of the problem was on the backend. Each request was sending a command using telnet. When both commands ran in rapid sequence (with one ajax request inside the other), the second telnet command would hang up because the first had not finished yet. Why exactly this led to a 500 error (which led me to believe the second ajax request was not communicating with the backend at all) I am not quite sure.

    A quick (and sloppy) fix is to add a timeout to make the second ajax wait a second before running.

    Like so:

      $.ajax({
          method: "POST",
          contentType: 'application/json',
          url: "/applynewsettings",
          data: JSON.stringify(settings)
      })
      .done(function(sysconfig_err_msg){
        $("#static-ip-input-err").html(sysconfig_err_msg);
    
        setTimeout(function(){ load_settings_config(); }, 1000);
    
      });
    

    A better (and more difficult) solution would be to make sure on the backend that it recognizes that telnet is being used by another command running, waits until it is complete, and then runs the second command and returns the data.

    No idea how to do that though. Ideas are welcome.