In-Browser-Objekt, das ein Proxy für ein Objekt auf einem NodeJS-Server ist

Wenn ich eine Funktion auf meinem NodeJS-Server habe, kann ich sie in nur wenigen Codezeilen für die Verwendung in einem Webbrowser verfügbar machen und sie dann mit $.getJSON() vom Browser aufrufen . Ein Beispiel dafür gebe ich weiter unten. Wenn ich diese Art der Nachrichtenübermittlung standardisieren möchte, gibt es viele Protokolle, an die ich mich halten könnte, wie z. B. JSON-RPC 2.0 , und APIs, die mir dabei helfen würden.

Jetzt habe ich etwa ein halbes Dutzend solcher Funktionen verfügbar gemacht, und mir kommt in den Sinn, dass es schön wäre, sie alle in einem einzigen Objekt zu bündeln, das auf einmal an den Server zu übergeben und dann automatisch ein Proxy-Objekt zu haben auf der Client-Seite erstellt, die mit dem Prototyp des Serverobjekts übereinstimmt und die Aufrufe von $.getJSON() kapselt. (Offensichtlich wäre dies nicht ohne Einschränkungen - eine synchrone Funktion wird nicht synchron bleiben, und jede Fehlerbehandlung sollte wahrscheinlich auf standardmäßige Weise erfolgen, um sich an Fehler anzupassen, die vom Message-Passing-Protokoll generiert werden.)

Kennen Sie eine Software, die so etwas wie diese Art von JavaScript-Objekt-Proxy für mich erledigen würde? Es scheint so etwas zu sein, was existieren könnte, obwohl mein Googeln nichts ergeben hat. Ich könnte es selbst schreiben, aber es würde lange dauern, es gründlich zu machen.

Wenn ich nicht einige Berichtsfunktionen auf der Clientseite geschrieben und dann entschieden hätte, dass sie wirklich dumm sind und auf den Server gehören, hätte ich möglicherweise nie den ungeschickten Unterschied in der Technik bemerkt, die ich derzeit benötige, um von diesen auf meine Kernfunktionalität zuzugreifen zwei Orte ;-)


Und hier ist das versprochene Beispiel, wie ich die Dinge im Moment mache: Wenn ich eine Funktion auf meinem NodeJS-Server habe ...

function myFunction(arg1, arg2) {
    return arg1 + arg2;
}

... dann kann ich es in nur wenigen Codezeilen für die Verwendung in einem Webbrowser verfügbar machen ...

var http = require('http');
var url = require('url');

http.createServer(function(request, response) {

    var parsed = url.parse(request.url, true);

    if (parsed.pathname == '/myFunction') {
        var result = myFunction(parsed.query.arg1, parsed.query.arg2);
        response.setHeader('Content-Type', 'application/json');
        response.setHeader('Access-Control-Allow-Origin', 'null');
        response.end(JSON.stringify(result));
    }

}).listen(8080, 'localhost');

...und rufe es dann aus dem Browser mit auf

    $.getJSON('http://localhost:8080/myFunction?arg1=Hello&arg2=World', function(result) {
        console.log(result);
    });

Antworten (2)

OK, also habe ich versucht, das selbst zu codieren.

https://github.com/Antony74/TransportManager/blob/master/sys/server/GenerateProxyApiSourceCode.js

Dieser nimmt meine Kern-API (die aus den Funktionen ‚selectSql‘, ‚getIndices‘ und ‚updateDatabase‘ besteht) und generiert daraus eine Proxy-API (siehe unten), die in eine Datei geschrieben und wie eine statische .js bereitgestellt wird -Datei wäre, und es ist ganz nett, mit ihr auf der Client-Seite zu arbeiten.

Ich bin definitiv bereit für andere Antworten von der Stange, da es sich immer noch so anfühlt, als hätte ich wahrscheinlich „das Rad neu erfunden“, und Feedback zu dieser Antwort ist ebenfalls willkommen.

// auto-generated code

function createCoreApiProxy() {       

    function improveErrorMessage(numberStatus, textStatus) {  
        if (textStatus == 'timeout')                
            textStatus = 'Request timed out';       
        else if (numberStatus == 0)                 
            textStatus = 'No response from server'; 

        console.log(textStatus);                    
        return textStatus;                          
    }                                               

    return {                                        
        selectSql: function(query, startRecord, schemaLevel, fnDone) {
            $.ajax(
            {
                type: 'POST',
                url: 'http://localhost:8080/selectSql',
                timeout: 6000,
                data: JSON.stringify({
                    query: query,
                    startRecord: startRecord,
                    schemaLevel: schemaLevel,
                }, null, 4),
                success: function(returnedData) {
                    fnDone(JSON.parse(returnedData));
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    fnDone({Error:improveErrorMessage(jqXHR.status, textStatus)});
                }
            });
        },
        getIndices: function(fnDone) {
            $.ajax(
            {
                type: 'POST',
                url: 'http://localhost:8080/getIndices',
                timeout: 6000,
                data: JSON.stringify({
                }, null, 4),
                success: function(returnedData) {
                    fnDone(JSON.parse(returnedData));
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    fnDone({Error:improveErrorMessage(jqXHR.status, textStatus)});
                }
            });
        },
        updateDatabase: function(arrPostedData, fnDone) {
            $.ajax(
            {
                type: 'POST',
                url: 'http://localhost:8080/updateDatabase',
                timeout: 6000,
                data: JSON.stringify({
                    arrPostedData: arrPostedData,
                }, null, 4),
                success: function(returnedData) {
                    fnDone(JSON.parse(returnedData));
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    fnDone({Error:improveErrorMessage(jqXHR.status, textStatus)});
                }
            });
        },
    };
}
Sie können Ihre eigene Antwort akzeptieren. Wenn es für Sie funktioniert, hilft es anderen, die die Frage in Zukunft lesen, wenn Sie es akzeptieren
@Mawg, danke, aber ich warte immer noch auf eine bessere Antwort. Oder, ich weiß nicht, vielleicht sogar das Verpacken eines Objekts als schlechte Idee abtun und stattdessen eine REST-API verpacken (z. B. devdactic.com/improving-rest-with-ngresource ).

Konzeptionell können Sie Ihren Code in zwei Komponenten unterteilen: Daten und Funktionen.

Hier gehe ich davon aus, dass die Daten statisch sind und auf dem Server generiert werden:

// server
data = [1,2,3,4,5]

Nun zu den Funktionen. Diese können sicherlich zwischen den Clients geteilt werden. Das Einfachste, was ich mir vorstellen kann, ist, den Quellcode der Funktionen als Strings zu senden und sie mit auf den Client zu laden eval.

Definieren Sie zunächst einige Funktionen auf dem Server:

// server
allData = function () {
  return data
}
evens = function () {
  return data.filter(function(num){
    return num % 2 == 0
  })
}

Jetzt eine Methode auf dem Server zum Senden von Daten + Funktionen. Ich verwende hier ein imaginäres Request-Handling-Framework; Sie können es in das von Ihnen verwendete übersetzen

  get('/dataAndFunctions', function(request){
    request.send(JSON.stringify({
      "data" => data,
      "sharedFunctions" => {
        "allData": allData.toString(),
        "evens": evens.toString()
      }
    }))
  })

Jetzt auf dem Client die Daten und Funktionen anfordern und laden. Verwenden von jQuery für dieses Beispiel

// client
$(function(){
  $.get("/dataAndFunctions", function(response){
    responseObject = JSON.parse(response)
    window.data = response.data
    Object.keys(responseObject.sharedFunctions).forEach(function(key){
      window[key] = eval(`(${sharedFunctions[key]})`)
    })
  })
})

Dann könnten Sie sowohl auf dem Client als auch auf dem Server anrufen evens()oder allData()und solange sich die Daten nicht ändern, beziehen sie sich auf identische Sammlungen.

Dies kann im Client-Code verwirrend aussehen:

window[key] = eval(`(${sharedFunctions[key]})`)

Wenn eval eine Funktion als Zeichenfolge übergeben wird, muss sie in ()Klammern eingeschlossen werden:

eval("(function(){return 1})")

Das Beispiel, das ich gegeben habe, verwendet ES6-Vorlagenzeichenfolgen, die durch Backticks getrennt sind und eine Interpolation enthalten${}