On Nov 13, 2014, at 12:59 PM, Alex Spencer <[email protected]> wrote:
> Imagine a simple route like this:
> 
> app.get('/test',
> apiCall1(),
> apiCall2(),
> apiCall3(),
> apiCall4(),
> apiCall5(),
> renderPage()
> 
> How would you prevent this code from being executed by the same client while 
> said client is spamming GET requests (either by refresh page or clicking 
> buttons, etc.) to the same route ?

Den torsdagen den 13:e november 2014 kl. 20:19:46 UTC+1 skrev Aria Stewart:
> I'd add another middleware at the head of the chain that keeps track of the 
> accesses by a given client (session ID? IP address? Identifying a client can 
> be its own problem, depending!), and errors (with next(an error here)) if the 
> user is over-requesting. Defining that is up to you.

On Nov 13, 2014, at 2:37 PM, Alex Spencer <[email protected]> wrote:
> But if a user hits Refresh (F5) twice instantly for example, I only want the 
> code to run once. But refreshing twice means two response objects. And I can 
> only send a valid response to the 2nd created response object. (The first one 
> wont work, according to tests) so I can't ignore requests either

Now that is an interesting case, and I think an ideal case for promises: In the 
first request for a client, create a promise and attach it AND its resolve 
function to some storage that will outlast the request. If it already exists, 
don't.

Call thepromise.then() with the renderPage code.

Call next() if you set up a new promise.

Let renderPage resolve that promise instead of rendering.

var promises = {};
var resolvers = {};
function startup(req, res, next) {
    var mainRequest = false;
    if (!promises[req.clientID]) {
        promises[req.clientID] = new Promise(function (resolve, reject) {
            resolvers[req.clientID] = { resolve: resolve, reject: reject };
            mainRequest = true;
        });
    }
    promises[req.clientID].then(function (data) {
        // This is where your rendering ACTUALLY goes.
        cleanup();
        res.render(data);
    }).catch(function (err) {
        cleanup();
        res.render('errors');
        // Note that you CAN'T call next(err) here since next has already been 
called in some branches. c'est la vie. You can work around it but it's ugly.
    });

    if (mainRequest) next();

    function cleanup() {
        delete promises[req.clientID];
        delete resolvers[req.clientID];
    }
}

And renderPage:

functiion renderPage(req, res, next) {
    resolvers[req.clientID].resolve(the data used to render);
}

And error handling:

function errorHandler(req, res, next, err) {
    resolvers[req.clientID].reject(err);
}

(that could be in your final apiCall5, but I'd probably factor it separately.)

What you've now done is joined these requests. They'll all complete when the 
promise is resolved.

Aria

Attachment: smime.p7s
Description: S/MIME cryptographic signature

Reply via email to