2016-04-13 7 views
2

Ich versuche Batch-API für meine Anwendung zu implementieren, und der Client kann senden eine Reihe von Anfrage an den Server und erwartet eine einzige Antwort zurück.Verarbeitung von Batch-REST-API-Anfragen im NodeJS-Express-Framework

Verwendung von Express-Framework in meiner MEAN-Stack-App. Ich habe eine Batch-API-Ressource erstellt, die diese vom Client erhält und gemäß der URL, die in jedem Teil der Anfrage erwähnt wird, agiert.

Musteranfrag Körper:

{ 
    "batchID": "abc123", 
    "batchReq": [{ 
     "id": "1", 
     "url": "/api/schoolm/students", 
     "method": "GET", 
     "value": null 
    }, { 
     "id": "2", 
     "url": "/api/schoolm/teachers", 
     "method": "POST", 
     "value": { 
      "name": "Teacher1", 
      "age": 34 
     } 
    }] 
} 

Dies wird gesendet/api/schoolm/Batch

Wie kann ich die Anfragen jeweils aus dem Innern der Batch-Controller an ihren jeweiligen API umleiten?

+0

was ist die Motivation mit 1 Anfrage in mehrere Anfrage aufgeteilt werden? – AJS

+0

Dies ist keine Anfrage - sie sind von verschiedenen Typen, die für verschiedene APIs gedacht sind. Wenn sie in einer einzigen Anfrage geclubed werden, reduziert sich die Anzahl von Anrufen, die ich von meiner Clientseite des Codes machen möchte. – swateek

+0

ich glaube, Ihr Ansatz ist möglicherweise nicht korrekt, sind Sie besorgt, wenn Knoten nicht die Menge der Anfragen, die Sie erwarten ?, wie eine einzige Instanz des Knotens mehr als fähig ist, die meisten Lasten zu handhaben, wenn es nicht können Sie skalieren mit Cluster, die in Knoten sehr einfach ist – AJS

Antwort

0

Meine Antwort ist stark geliehen/inspiriert/aus clay's Antwort genommen. Und ich füge nur alle Details hinzu, die ich in meinem Fall verwendet habe. Danke nochmal Ton!

Zuerst ist hier, wie meine API strukturiert:

/api/schoolm die api Elternteil

Und von dort Anfragen weitergeleitet werden auf das Kind APIs:

/api/schoolm/students/ 
/api/schoolm/teachers/ 
/api/schoolm/subjects/ 
... 

nun Batch opertaions zu unterstützen, ich eine weitere API hinzugefügt, die für die Verarbeitung aller Batch-Transaktionen zuständig wäre.

/api/schoolm/batch/ 

Eine typische Batch-Anfrage würde wie folgt aussehen:

{ 
"batchReq":[ 
    { 
    "id":"1", 
    "uri": "/api/schoolm/students/", 
    "httpMethod": "GET" 
    }, 
    { 
    "id":"2", 
    "uri": "/api/schoolm/teachers/", 
    "httpMethod": "GET" 
    } 
] 
} 

Der Controller bei /api/schoolm/batch die (Business-Logik) Anforderung validiert und mit einer Referenz-ID antwortet zurück.

var async = require('async'); 
var httpMocks = require('node-mocks-http'); 
var shortid = require('shortid'); 

exports.processBatch = function(batchRequests, callback){ 


    var batchRes = {}; 
    var result = {}; 

    var batchID = shortid.generate(); 
    result.batchID = batchID; 

    async.setImmediate(function() { 
    async.eachSeries(batchRequests, function(batchReq, callback){ 
     var mockRes = httpMocks.createResponse({ 
     eventEmitter: require('events').EventEmitter 
     }); 

     var reqDetails = _processSubRequest(batchReq); 

     _fetchResponse(reqDetails, mockRes); 
     mockRes.on('end',function(){ 
     result[batchReq.id] = mockRes._getData(); 
     callback(null); 
     }); 

    },function(err){ 
     console.log(result); 
    }); 
    }); 

    callback("Your batch request has been accepted. Request ID - "+batchID); 
} 

function _processSubRequest(batchReq){ 
    var retValue = {}; 
    var request = {}; 
    var uriComp = batchReq.uri.split('/'); 
    retValue.controller = uriComp[3]; 

    request.method = batchReq.httpMethod; 
    request.url = batchReq.uri; 
    request.body = batchReq.body || {}; 
    request.params = batchReq.params || {}; 

    if(batchReq.httpMethod == 'GET'){ 
     if(uriComp.length > 5){ 
     request.params.id = uriComp[4]; 
     } 
    }else if(batchReq.httpMethod == 'POST'){ 

    }else{ 

    } 
    retValue.request = request; 
    return retValue; 
} 

function _fetchResponse(reqDetails, mockRes){ 
    var mockReq = httpMocks.createRequest(reqDetails.request); 
    // proceed to fetch answer from respective controller methods. 
    ....................... 
} 

function _prepResponse(result, err, res){ 
    var data = {}; 

    if(err) { 
    data["error"] = err; 
    data["response"] = null; 
    return data; 
    }else{ 
    data["error"] = null; 
    data["response"] = result; 
    return data; 
    } 
} 

Nachdem die Stapelverarbeitung abgeschlossen ist, kann es für den Benutzer als Teil der Live-Benachrichtigung oder gespeichert in DB von später zu lesende gesendet werden.

1

Klingt so, als müssten Sie Ihre Routing-Tabellen doppelt definieren. Sie könnten Ihre Logik ein wenig aufbrechen, damit Sie leichter auf die Funktionen zugreifen können. Definieren Sie eine StudentsController und TeacherController, so können Sie Express Routen wie

app.get('/api/schoolm/students', studentController.getStudents); 
app.post('/api/schoolm/teachers', teachController.postTeachers); 

definieren, aber sie immer noch in Ihrer Dosierung Methode zugreifen. EDIT: Sie möchten wahrscheinlich die Hauptlogik noch mehr auseinander brechen, so dass Sie die Batching-Methoden direkt aufrufen können.

StudentController.prototype.getStudents = function(params, callback){ 
    //do stuff 
    callback(null, studentList); 
} 

StudenController.prototype.getStudentsRequest = function(req, res){ 
    this.getStudents({}, function(err, students){ 
    //check error 
    res.send(students); 
    }); 
} 

//elsewhere, in routing table 
app.get('/api/schoolm/students', studentController.getStudentsRequest); 

//elsewhere, in batching method 
//api route that matches students 
studentController.getStudents(batchReq[index].value, function(err, students){ 
    //do something with students, like put in outer results object. 
    //call async.each's callback(), or whatever for control flow 
}); 

Alternativ können Sie gefälschte req und res Objekte machen direkt die Controller zu nennen. Etwas wie node-mocks-http.

Natürlich müssten Sie die im Stapelverarbeitungsobjekt angegebenen Pfade erneut analysieren, damit Sie die richtige Controller-Methode aufrufen und die Ergebnisse speichern können. Vielleicht würde etwas wie director (oder was auch immer Express intern verwendet) helfen.

Ihre typische Routen in Express würden ihre Antwort am Ende senden, und dass res.sendres.end auch nennen könnte, so kann man nicht wirklich sie um sie an anderer Stelle weitergeben oder verwenden (wie Sie nicht res.end zweimal aufrufen können).

Obwohl eigentlich, warum nicht einfach mehrere Anfragen vom Client senden. Dann müssen Sie auch keine Stapelantwort auf dem Client analysieren. Ich denke, das macht Ihre

+0

Ich bin neugierig, wie Google es Batch-APIs versucht, etwas ähnliches anzuwenden. Selbst Salesforce macht es. Aber alles, was sie haben, ist, wie die Client-Anfrage gesendet werden sollte, niemand spricht darüber, wie es im Backend verarbeitet wird. – swateek

+0

Am Ende müssen Sie die richtige Funktion für jedes Element im Batch aufrufen, was im Grunde nur Regex-Parsing ist. Wenn Sie Pfadparameter haben, möchten Sie etwas wie Director oder [route-parser] (https://www.npmjs.com/package/route-parser), um das zu erleichtern. Achten Sie darauf, wie lange Stapel verarbeitet werden (oder wie viele Elemente im Stapel vorhanden sein können), damit die ursprüngliche Anforderung während des Wartens nicht abläuft. – clay

+0

Haben sich um diesen Teil gekümmert, asynchrone Verarbeitung und eine Benachrichtigung, um den Benutzer über das Ende des Prozesses zu benachrichtigen. – swateek

0

Schalten Sie GraphQL für Batch-Anfrage :)

Verwandte Themen