【问题标题】:Processing Batch REST API requests in NodeJS Express framework在 NodeJS Express 框架中处理批处理 REST API 请求
【发布时间】:2016-08-04 06:01:23
【问题描述】:

我正在尝试为我的应用程序实现批处理 API,并且客户端可以向服务器发送一组发送请求并期望返回单个响应。

在我的 MEAN 堆栈应用程序中使用 Express 框架。我创建了一个批处理 API 资源,该资源从客户端接收,并且应该按照请求的每个部分中提到的 URL 执行操作。

示例请求正文:

{
    "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
        }
    }]
}

这是发送到 /api/schoolm/batch

如何将批处理控制器内部的请求重定向到各自的 API?

【问题讨论】:

  • 将1个请求分成多个请求的动机是什么?
  • 这不是一个请求——它们是针对不同 API 的不同类型。将它们合并到一个请求中是为了减少我想从代码的客户端进行的调用次数。
  • 我相信您的方法可能不正确,您是否担心节点无法处理您期望的请求数量?因为单个节点实例能够处理大多数负载,如果您不能使用在节点中也很容易的集群进行扩展
  • 哦,不。这不是问题。我们预计不会有那么高的流量。但是,如果他们需要来自多个 API 的数据以在一个屏幕上显示,则为 UI 提供各种灵活性。例如,这些可以合并到一个请求中。

标签: node.js rest http express mean-stack


【解决方案1】:

听起来您需要双重定义路由表。您可以稍微打破逻辑,以便更轻松地访问这些功能。定义一个StudentsControllerTeacherController,这样你就可以定义Express路由像

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

但仍然可以在您的批处理方法中访问它们。 编辑:您可能想进一步分解主要逻辑,因此您可以直接调用批处理方法。

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
} );

或者,您可以制作假的 reqres 对象来直接调用控制器。类似node-mocks-http

当然,您必须重新解析批处理对象中命名的路径,以便调用正确的控制器方法并存储结果。也许像director(或内部使用的任何快递)之类的东西会有所帮助。

您在 Express 中的典型路线将在最后发送它们的响应,并且 res.send 也可以调用 res.end,因此您不能真正传递它们或在其他地方使用它们(因为您不能调用 res.end两次)。

虽然真的,为什么不直接从客户端发送多个请求。然后,您也不必在客户端上解析出批处理响应。我认为这会让你的

【讨论】:

  • 我很好奇谷歌如何处理它的批处理 API 想要应用类似的东西。甚至 Salesforce 也这样做。但是他们所拥有的只是客户端请求应该如何发送,没有人谈论它在后端是如何处理的。
  • 最后,您必须为批处理中的每个项目调用正确的函数,这基本上只是正则表达式解析。如果你有路径参数,你会想要像 director 或 route-parser 这样的东西来让这更容易。注意处理批次需要多长时间(或批次中可以存在多少项目),以免原始请求在等待时超时。
  • 已经处理了这部分,异步处理和通知机制,以通知用户进程结束。
  • 我正在研究批处理,因为一旦我一次性处理完所有请求,我只需要调用本地方法而不是像客户端那样调用 HTTP 方法。
  • 听起来你明白了。希望这对您有帮助。
【解决方案2】:

我的回答大量借用/启发/取自 clay's 回复。我只是添加了我在我的案例中使用的所有细节。再次感谢粘土!

首先是我的 API 的结构:

/api/schoolm 是父 api

然后将请求从那里重定向到子 API:

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

现在,为了支持批处理操作,我添加了另一个 API 来负责处理所有批处理事务。

/api/schoolm/batch/

典型的批处理请求如下所示:

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

/api/schoolm/batch 的控制器验证请求(业务逻辑)并返回参考 ID。

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;
  }
}

批处理完成后,可以作为实时通知的一部分发送给用户,也可以存储在数据库中以供以后读取。

【讨论】:

    【解决方案3】:

    为批量请求打开GraphQL :)

    【讨论】:

      猜你喜欢
      • 2014-07-06
      • 2023-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-14
      • 2018-07-31
      • 1970-01-01
      • 2018-12-04
      相关资源
      最近更新 更多