JSON-String per HTML Post

  • JSON-String per HTML Post

    Edit: Ich habe zumindest eine Lösung gefunden. Mit myString.replace(/\\/g, "") entferne ich alle Backslashes und bekomme dadurch wieder das JSON-Format.

    Hallo,

    ich versuche schon seit zwei Stunden einen JSON-String per Post-Request an den Server zu schicken um dann dort per req.body mit den Daten weiterarbeiten zu können. Der String wird vernünftig erzeugt, da er im Inputfeld korrekt angezeigt wird. Scheinbar werden jedoch '/' eingefügt.

    HTML-Quellcode: Post-Form

    1. <form action="/result" method="post" enctype="text/plain" autocomplete="off">
    2. <script>
    3. </script>
    4. <input id="obj" name="obj" value="">
    5. <button type="button" name="action" value="getResult" onclick="generateJSON(true)">generateJSON </button>
    6. <button>Generiere Knoten</button>
    7. </form>


    JavaScript-Quellcode: generateJSON()

    1. function generateJSON(loaded){
    2. if(loaded) {
    3. var stuff = {nodes: localData, edges: localEdges};
    4. stuff = JSON.stringify(stuff);
    5. console.log("Ergebnis?:" + stuff);
    6. $('#obj').val(stuff);
    7. // $('form').submit();
    8. }
    9. }

    JavaScript-Quellcode: app.js

    1. app.post('/result', urlencodedParser, function(req, res){
    2. var test = JSON.stringify(req.body.obj);
    3. console.log("Fertiges JSON?: " + req.body.obj);
    4. console.log("Fertiges JSON?: " + req.body.nodes);
    5. console.log("Fertiges JSON?: " + test);
    6. res.send('cps_success');
    7. });

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Shiroco ()

  • Das ganze scheint mir wenig durchdacht.

    Erstmal zur Reparatur deines Szenarios:

    Entferne erst mal aus dem "form"-Tag das "enctype"-Attribut. Du willst schließlich urlencoded versenden. (Das entnehme ich dem Parser in der app.js)

    req.body.obj enthält jetzt den JSON-String. Um daraus ein Objekt zu erzeugen, muss dieser erst mal geparsed werden:

    JavaScript-Quellcode: app.js

    1. app.post('/result', urlencodedParser, function(req, res){
    2. req.body.obj = JSON.parse(req.body.obj);
    3. var test = JSON.stringify(req.body.obj);
    4. console.log("Fertiges JSON?: " + req.body.obj);
    5. console.log("Fertiges JSON?: " + req.body.nodes);
    6. console.log("Fertiges JSON?: " + test);
    7. res.send('cps_success');
    8. });
    Die "\" die dir aufgefallen sind, sind nur escape-Sequenzen für die Anführungszeichen im String.


    Bei deiner Lösung codierst du die Daten erst in JSON, nur um sie kurz darauf gleich noch mal urlconform zu codieren. Das ist doch doppelter Aufwand.



    Nun zu einer besseren Lösung:

    Anbieten würde sich hier natürlich wieder AJAX. Da du JQuery verwendest, werde ich das auch mal:

    HTML-Quellcode

    1. <body>
    2. <form action="/result" method="POST">
    3. <button>Generiere Knoten</button>
    4. </form>
    5. <script>
    6. $('form').on('submit', function(e) {
    7. e.preventDefault()
    8. var $form = $(this)
    9. $.post($form.attr('action'), {
    10. nodes: localData,
    11. edges: localEdges
    12. }).done(function(data) {
    13. $('body').html(data)
    14. })
    15. })
    16. </script>
    17. </body>
    Alles anzeigen


    Das Ganze wird auch hier urlencoded versendet. Wenn du neben den generierten Daten auch noch form inputs senden möchtest, sähe das ganze beispielsweise so aus:

    HTML-Quellcode

    1. <body>
    2. <form action="/result" method="POST">
    3. <input type="text" name="optional">
    4. <button>Generiere Knoten</button>
    5. </form>
    6. <script>
    7. $('form').on('submit', function(e) {
    8. e.preventDefault()
    9. var $form = $(this)
    10. $.post($form.attr('action'), $form.serialize() + '&' + $.param({
    11. nodes: 'localData',
    12. edges: 'localEdges'
    13. })).done(function(data) {
    14. $('body').html(data)
    15. })
    16. })
    17. </script>
    18. </body>
    Alles anzeigen


    Es gibt auch Möglichkeiten den request JSON codiert zu versenden, aber lies dir hierzu bitte die JQuery Dokumentation durch. Wenn du möchtest, zeige ich dir auch noch eine Version ohne JQuery.


    Wenn du kein AJAX möchtest, könntest du auch so was machen:


    HTML-Quellcode

    1. <body>
    2. <form action="/result" method="POST">
    3. <div style="display: none;"></div>
    4. <button>Generiere Knoten</button>
    5. </form>
    6. <script>
    7. $('form').on('submit', function() {
    8. var $div = $(this).find('div').first()
    9. $div.empty()
    10. $div.append($('<input type="hidden" name="nodes" value="' + localData + '">'))
    11. $div.append($('<input type="hidden" name="edges" value="' + localEdges + '">'))
    12. })
    13. </script>
    14. </body>
    Alles anzeigen
  • Ich hatte völlig vergessen, dass ich unter anderem auch hier nach Hilfe geschrien hatte. Ich hatte irgendwann in der Nacht eine Lösung gefunden und bin dann ins Bett gefallen - Sorry, Ich habe letztendlich (auf einen Hinweis hin) "einfach" alle escape-Zeichen aus dem String gelöscht und danach funktioniere es einwandfrei. Warum diese Zeichen eingefügt wurden konnte mir der Tippgeber nicht erklären. Zumindest ist mir nun klarer was ich falsch gemacht habe. Vielen Dank dafür. Ich arbeite noch immer an dem Programm. Momentan versuche ich das ganze in zwei Frames darzustellen. Im ersten Frame soll es möglich sein das Netzwerk zu erstellen und im zweiten Frame soll das Ergebnis nach einem Click auf einen Button dargestellt werden.

    Wenn ich AJAX richtig verstehe bietet sich dies hierfür an richtig?
  • Bin erst vor kurzem wieder dazu gekommen an der Simulation weiter zu arbeiten. Ich habe es versucht mit xmlHTTPRequest zu lösen. In der .ejs Datei habe ich folgende Funktionen implementiert:

    Quellcode

    1. var xmlHttp = createXmlHttpRequestObject();
    2. //Setup-Funktion
    3. function createXmlHttpRequestObject(){
    4. var xmlHttp;
    5. if(window.ActiveXObject){
    6. try{
    7. xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    8. }catch(e){
    9. xmlHttp = false
    10. }
    11. }else{
    12. try{
    13. xmlHttp = new XMLHttpRequest();
    14. }catch(e){
    15. xmlHttp = false
    16. }
    17. }
    18. if(!xmlHttp){
    19. alert("XMLHTTP-Object creation failed!");
    20. } else {
    21. return xmlHttp;
    22. }
    23. }
    24. //Wird durch den "Simulate"-Button ausgelöst
    25. function simulateCPS(){
    26. if(xmlHttp.readyState==0 || xmlHttp.readyState==4){
    27. xmlHttp.open("POST", "/sideSim", true);
    28. xmlHttp.onreadystatechange = handleServerResponse;
    29. var stuff = [{nodes: localData}, {edges: localEdges}];
    30. xmlHttp.send(stuff);
    31. }else {
    32. setTimeout('process()', 1000);
    33. }
    34. }
    35. function handleServerResponse(){
    36. if(xmlHttp.readyState==4){
    37. if(xmlHttp.status==200){
    38. var xmlResponse = xmlHttp.responseXML;
    39. var xmlDocumentElement = xmlResponse.documentElement;
    40. var message = xmlDocumentElement.firstChild.data;
    41. var content = document.getElementsById("right");
    42. document.getElementById("Tree").value = message;
    43. }
    44. }
    45. }
    Alles anzeigen


    In der Funktion "handleServerResponse" wird zu Testzwecken zunächst nur der Text eines Labels verändert. In app.js sollte eigentlich die Funktion app.post('/sideSim', urlencodedParser, function (req,res) aufgerufen werden. Diese wird jedoch nicht ausgeführt. Ich bekomme als Ausgabe in der Konsole nur die Meldung "POST 192.168.2.196:3000/Sidesim 500 (Internal Server Error)". Irgendwas mache ich beim handling des POST-Requests falsch.

    Quellcode

    1. app.post('/sideSim', function (req,res) {
    2. var myJSON = JSON.stringify(req.body.data);
    3. myJSON = myJSON.replace(/\\/g, "");
    4. myJSON = myJSON.slice(1);
    5. myJSON = myJSON.slice(0, -1);
    6. console.log("Fertiges JSON?: " + myJSON);
    7. myJSON = JSON.parse(myJSON);
    8. console.log("Fertiges JSON?: " + myJSON);
    9. console.log("Fertiges JSON?: " + myJSON[0].nodes[0].id);
    10. jsonfile.writeFile(file, myJSON, function (err) {
    11. console.error(err)
    12. });
    13. fs.readFile('./cps.json', 'utf8', function (err, data) {
    14. if (err) {
    15. throw err;
    16. } else {
    17. var cps = JSON.parse(data);
    18. //Zeigt alle Gespeicherten Objekte an
    19. //console.dir(data);
    20. //Hier werden alle Knoten ausgelesen. Entsprechend ihrer Rolle wird ein Objekt erzeugt und dem
    21. //cpsNodes-Array hinzugefügt. Dieses Array beinhaltet nach der Schleife alle Knoten des zu simulierenden
    22. //Netzwerks.
    23. // Single for loop (i)
    24. // i loop
    25. loop(0, function(i){return i<cps[0].nodes.length;}, function(i){return i+1;},
    26. function(i, next){
    27. // looping.
    28. if (cps[0].nodes[i].role == "sensor") {
    29. //JS-Objekt mit den Eigenschaften des JSON-Objekts wird erstellt (SensorNodes)
    30. var newSensorNode = extend(new sensorNode(), cps[0].nodes[i]);
    31. newSensorNode.createServer();
    32. //Erstelltes JS-Objekt wird dem cpsNodes-Array hinzugefügt
    33. cpsNodes.push(newSensorNode);
    34. //Kontrollausgabe
    35. console.log("SensorNode mit der ID: %s", cpsNodes[i].id);
    36. } else if (cps[0].nodes[i].role == "aggregator") {
    37. //JS-Objekt mit den Eigenschaften des JSON-Objekts wird erstellt (SensorNodes)
    38. var newAggregatorNode = extend(new aggregatorNode(), cps[0].nodes[i]);
    39. newAggregatorNode.createServer();
    40. //Erstelltes JS-Objekt wird dem cpsNodes-Array hinzugefügt
    41. cpsNodes.push(newAggregatorNode);
    42. //Kontrollausgabe
    43. console.log("AggregatorNode mit der ID: %s", cpsNodes[i].id);
    44. } else {
    45. //JS-Objekt mit den Eigenschaften des JSON-Objekts wird erstellt (SensorNodes)
    46. var newActuatorNode = extend(new actuatorNode(), cps[0].nodes[i]);
    47. newActuatorNode.createServer();
    48. //Erstelltes JS-Objekt wird dem cpsNodes-Array hinzugefügt
    49. cpsNodes.push(newActuatorNode);
    50. //Kontrollausgabe
    51. console.log("ActuatorNode mit der ID: %s", cpsNodes[i].id);
    52. }
    53. next();
    54. },
    55. function(i){
    56. //Schleife i ist beendet
    57. console.log("Filled cpsNodes!");
    58. // 2-Level deep for loop (i,j)
    59. // i loop
    60. loop(0, function(j){return j<cpsNodes.length;}, function(j){return j+1;},
    61. function(j, next){
    62. // j loop
    63. var established_connections = 0;
    64. loop(0, function(k){return k<cps[1].edges.length;}, function(k){return k+1;},
    65. function(k, next){
    66. // looping.
    67. if (cpsNodes[j].id == cps[1].edges[k].fromNodeId) {
    68. if (cpsNodes[j].role == "sensor") {
    69. // console.log("Verbindung von Node %s zu Node %s", cps[1].edges[k].fromNodeId, cpsNodes[cps[1].edges[k].toNodeId - 1].id );
    70. // console.log("Von Port %s zu Port %s", cpsNodes[j].port, cpsNodes[cps[1].edges[k].toNodeId - 1].port);
    71. cpsNodes[j].openConnection(cpsNodes[cps[1].edges[k].toNodeId - 1].port, HOST);
    72. established_connections++;
    73. }
    74. else if(cpsNodes[j].role == "aggregator") {
    75. // console.log("Verbindung von Node %s zu Node %s", cps[1].edges[k].fromNodeId, cpsNodes[cps[1].edges[k].toNodeId - 1].id );
    76. // console.log("Von Port %s zu Port %s", cpsNodes[j].port, cpsNodes[cps[1].edges[k].toNodeId- 1].port);
    77. cpsNodes[j].openConnection(cpsNodes[cps[1].edges[k].toNodeId - 1].port, HOST);
    78. }
    79. }
    80. //console.log('Innerloop for Node %i: %i',j,k);
    81. next();
    82. },
    83. function(j){ next(); } // note: this next is the parent's next. yay!
    84. );
    85. },
    86. function(j){
    87. //outer-loop done?
    88. //Nachdem alle Knoten erstellt und alle Verbindungen von Sensoren zu anderen Knoten
    89. //eingerichtet wurden kann nun mit dem versenden der Sensordaten begonnen werden.
    90. for(k=0; k<cpsNodes.length;k++){
    91. if(cpsNodes[k].role == 'sensor'){
    92. cpsNodes[k].sendData();
    93. }
    94. }
    95. setTimeout(function () {
    96. var treeData = ifm.getData();
    97. console.log('TreeData:');
    98. console.log(JSON.stringify(treeData));
    99. res.send(JSON.stringify(treeData));
    100. }, 500);
    101. }
    102. );
    103. }
    104. );
    105. }
    106. });
    107. });
    Alles anzeigen
  • Shiroco schrieb:

    Ich bekomme als Ausgabe in der Konsole nur die Meldung "POST 192.168.2.196:3000/Sidesim 500 (Internal Server Error)".
    Das sieht nach dem Log des Klienten aus. Mich würde mal interessieren, was im Server-Log steht.


    Shiroco schrieb:

    var stuff = [{nodes: localData}, {edges: localEdges}];

    xmlHttp.send(stuff);
    Ich glaube nicht, dass das so geht. .send() akzeptiert, glaube ich, nur DOM, Strings und Formdata. Gibt es einen Grund, wieso das ein Array ist?
    Ich würde es ja als JSON verschicken:

    JavaScript-Quellcode

    1. xmlHttp.setRequestHeader('Content-Type', 'application/json')
    2. xmlHttp.send(JSON.stringify({ nodes: localData, edges: localEdges }))
    Du müsstest hierfür natürlich auf der Serverseite middleware/parser anpassen.



    PS: Mir persönlich ist der Umgang mit der XMLHttpRequest-API zu blöde. Ich verwende lieber die neue Fetch-API. Um auch ältere Browser zu unterstützen, die diese noch nicht implementiert haben, binde ich einfach Polyfill.io in die Seiten ein.
  • Wieder viel zu spät, aber dennoch erstmal Danke für deine Hilfe. Ich habe den Fehler mittlerweile gefunden. Deinen Vorschlag hatte ich bei mir bereits implementiert, aber dies löste mein Problem auch nicht. Es war eine Kombination aus copy/paste von altem Code und das ich auf Client-Seite xmlHttp.responseXML genutzt habe (Dies wurde in allen Beispielen die ich gesehen hatte so gemacht). Der alte Code beinhaltete meinen Workaround für das Problem mit den escape slashes. Jedoch gab es keine Fehlermeldung auf Serverseite und ich ging von einem Problem im Client aus. Erst nachdem ich die Funktion neu geschrieben habe bemerkte ich wo der Fehler lag. Das andere Problem war das ich einfach nicht nachgedacht habe, dass responseXML evnt. möglicherweise vielleicht.... ein bestimmtes Format erwartet..... Nachdem ich auf die glorreiche Idee kam mir einfach nur .response ausgeben zu lassen war auch dies gelöst. Mittlerweile läuft das Programm fast genauso wie ich es will. Es gibt noch zwei Probleme die ich momentan versuche zu lösen.

    Zum einen will ich das die Dropdown-Checkboxen beim Aufruf für einen jeweiligen Knoten nur die möglichen Optionen für diesen Knoten anzeigt und welche von diesen aktuell ausgewählt sind. Ein anderes Problem ist das schließen aller Server sowie deren Verbindungen. Entweder gibt es einen Fehler bezüglich des JSON-Files (unexepected end) oder aber irgendwelche Verbindungen sind geschlossen obwohl sie geöffnet sein müssten. Ich schließe alle Server und zerstöre alle Verbindungen, da ich anderenfalls logischerweise das Problem bekomme das Ports bereits in Verwendung sind.
  • Das Problem das beim zweiten Durchlauf des Programms Fehler beim lesen des JSON-Files auftreten konnte ich beheben. Ich hatte writeFile und readFile sequentiell im Code. Dadurch konnte es im zweiten Durchlauf dazu kommen, dass von JSON-File gelesen wurde bevor writeFile beendet war. writeFile führt nun einen Callback auf readFile aus, wenn die Operation abgeschlossen ist.

    Die asynchrone Ausführung des Codes macht mir zwar noch immer zu schaffen, aber es geht weiter. Wahrscheinlich handelt es sich bei den Problemen mit geschlossenen Servern/Verbindungen um ähnlich gelagerte Probleme.
  • Ich habe nun den Code von app.js überarbeitet und den Aufbau des Netzwerks sowie das Schließen der Server über Funktionen mit Callbacks geregelt . Nun funktioniert das Schließen von Verbindungen und Servern einwandfrei. Es ist nun alles sehr viel übersichtlicher ;) Gibt es eigentlich eine bessere Möglichkeit neben setTimeout() um darauf zu warten bis das erstellte Netzwerk alle Nachrichten verschickt hat? Ich hatte folgendes versucht (Pseudocode):

    Quellcode

    1. var responseData = getData();
    2. res.send(responseData);

    Jedoch wird dann einfach null verschickt. Meine Lösung funktioniert, aber sollte das erstellen des Netzwerkes und das verschicken der Daten länger als mein Timeout dauern wird auch hier null verschickt.

    Was mir noch immer Probleme bereitet ist der Part mit dem Dropdown-Menü inklusive Checkboxen. Ich habe schon verschiedene Varianten versucht damit die verfügbaren Checkboxen dynamisch angepasst werden, aber der Code scheint einfach nichts zu bewirken.

    Steht im <Body> und ist Teil eines <Div>

    JavaScript-Quellcode

    1. <label for="requiresData">requiresData:</label>
    2. <select id="reqDataList" multiple="multiple" name="reqDataList">
    3. <optgroup label="Connected Nodes">
    4. <%for (var i=0;i<dataSet.length;i++){%>
    5. <% var node='Node-ID: ' %>
    6. <% node+= dataSet[i].id %>
    7. <option value="<%= dataSet[i].port %>"> <%= node %> </option>
    8. <%}%>
    9. </optgroup>
    10. </select>

    Befindet sich zwischen /body und /html

    JavaScript-Quellcode

    1. <script id="dropdown">
    2. $('#reqDataList').multiselect({
    3. enableClickableGroups: true
    4. });
    5. </script>

    Dieser Code erstellt für alle Knoten in dataSet eine Checkbox im Dropdown-Menü. Schöner wäre es wenn nur die Knoten angezeigt würden, die auch mit dem ausgewählten Knoten (Div wird für einen vom Nutzer ausgewählten Knoten angezeigt) verbunden sind. Ich hatte nun versucht in der Methode die das Div anzeigt (befindet sich in <Head>) die Daten zu aktualisieren, aber dies scheint nicht zu funktionieren.
  • Shiroco schrieb:

    Gibt es eigentlich eine bessere Möglichkeit neben setTimeout() um darauf zu warten bis das erstellte Netzwerk alle Nachrichten verschickt hat?
    Um mit asynchronität umzugehen, gibt heutzutage nichts besseres als Promises. Wenn du zudem noch die neuste Node Version (>7.6) verwendest, kannst du auch noch async/await verwenden. Dein Beispiel würde dann mit einer winzigen Änderung funktionieren:

    JavaScript-Quellcode

    1. var responseData = await getData();
    2. res.send(responseData);


    Shiroco schrieb:

    Was mir noch immer Probleme bereitet ist der Part mit dem Dropdown-Menü inklusive Checkboxen. Ich habe schon verschiedene Varianten versucht damit die verfügbaren Checkboxen dynamisch angepasst werden, aber der Code scheint einfach nichts zu bewirken.
    Da deine Seite ja scheinbar Server seitig gerendert wird, müsstest du entweder dort schon das dataSet gefiltert übergeben oder Clientseitig gezielt Daten mittels JS ausblenden (mit dem style "display: none;").