0

Is the anyway to add some middleware to specific actions? because as far as I know addPreProcessor adds the middleware to all the actions? lets say you don't want to have authentication or some other checks on some actions, is there any solution?

I have a short term solution but it would be nice if you could assign your specific middlewares when you're defining your action(like giving an array of middleware names that need to be run, in order)

My current solution is keeping an array of all the actions that I need to apply the middleware to them and then check it against the connection.aciton, but then still every single request goes through all the middlewares and then it gets passed around which it doesn't sound efficient to me!

exports.middlewares = function(api, next){

    var myImportantMiddleware = function(connection, actionTemplate, next) {

        var actionsToBeChecked = ['deposit'];
        var action = connection.action;

        if(actionsToBeChecked.indexOf(action) > -1) {
                /*  middleware logic 
                next(connection, true); */
        } else {
            next(connection, true);
        }
    }

    api.actions.addPreProcessor(myImportantMiddleware);

    next();
}

Thanks in advance !

scniro
  • 16,844
  • 8
  • 62
  • 106
DanialK
  • 1
  • 1

2 Answers2

1

No need to do all that! Check out this example from the docs: https://github.com/evantahler/actionhero-tutorial/blob/master/initializers/middleware.js

exports.middleware = function(api, next){

  var authenticationMiddleware = function(connection, actionTemplate, next){
    if(actionTemplate.authenticated === true){ // <-- HERE
      api.users.authenticate(connection.params.userName, connection.params.password, function(error, match){
        if(match === true){
          next(connection, true);
        }else{
          connection.error = "Authentication Failed.  userName and password required";
          next(connection, false);
        }
      });
    }else{
      next(connection, true);
    }
  }

  api.actions.addPreProcessor(authenticationMiddleware);

  next();
}

Yes, all middlewares fire for all actions, but you can tell the middleware to inspect the action's definition, and look for a specific property. In this case, we only care if the action looks like this:

exports.randomNumber = {
  name: 'randomNumber',
  description: 'I am an API method which will generate a random number',
  outputExample: {
    randomNumber: 0.123
  },

  authenticated: true // <<--- HERE

  run: function(api, connection, next){
    connection.response.randomNumber = Math.random();
    next(connection, true);
  }

};
Evan
  • 3,191
  • 4
  • 29
  • 25
  • Thanks Evan. Although I still think for the big projects that have many middlewares it's better if every request don't go through all the middlewares and also you can change the order of execution of middlewares for different actions by just just adjusting the values inside of the array. I modified my processor to work with pre and post middlewares and it's working great in our application ! – DanialK Jan 11 '15 at 08:28
  • I generally disagree, as it's a very fast `O(n)` (one loop) lookup to check if your action definition has a specific property or not. The composilbilty this way is the most flexible. But that's jus tmy opinion :D If you have a proposed change, please take it to mailing list! – Evan Jan 11 '15 at 22:17
  • I know It might not slow the process that much, but i'm just too used too the way express handles middlewares by specifying middlewares for each route in order. But the cool thing you can do with actionhero is the post-processors which express doesn't have and it's a very nice future for our applicaiton !! Yup definitely i'm gonna make a new topic soon, the code below has some bugs and it's not doing anything for post-processors, i'll put the final version that is working for me there :) – DanialK Jan 12 '15 at 04:42
-1

Ok I think I found a better solution and I would like to know your opinion.

So I just added middlewares : ['middlewareOne', 'middlewareTwo'] to my actions, which the order of execution is equal to order of middleware names in the array and then my middlewares initialiser is like this

var async = require('async');

exports.middlewares = function(api, next){

    var middlewares = {

        authMiddleware : function(connection, next){
            /* Middleware logic */
        },

        premiumAccessMiddleware : function(connection, next){
            /* Middleware logic */
        },

        adminAccessMiddleware : function(connection, next){
            /* Middleware logic */
        }

    };


    var middlewareProcessor = function(connection, actionTemplate, next){

        var actionMiddlewares = actionTemplate.middlewares;

        async.eachSeries(actionMiddlewares, function(middlewareName, callback){

            var middleware = api.middlewares[middlewareName];

            if(!middleware) throw (new Error("Middleware '"+ middlewareName +"'doesn't exist"));  /* In case I had a typo */

            middleware(connection, function(new_connection, toRender){

                connection = new_connection;

                if(toRender){

                    callback();

                }else{

                    callback('YOU SHALL NOT PASS');
                }

            });

        }, function(err){

            // if(err) return next(connection, false); // see EDIT1

            next(connection, true);

        });

    }

    api.actions.addPreProcessor(middlewareProcessor);

    next();
} 

Any thoughts?

EDIT1: next(connection, false); don't send anything to user, I guess you always want to send an error response or something to the user even if the execution middlewares stop after one of them wasn't successful and returned a next(connection, false). In this case in the eachSeries final callback function I guess we always have to use next(connection, true); !!

DanialK
  • 1
  • 1
  • Doesn't this still loop though all middleware for each action? You will process the normal middlware loader once and hit your `middlewareProcessor`, but then you async-loop though all 3. The normal processor will also stop excecution if a previous one halts:https://github.com/evantahler/actionhero/blob/master/initializers/actionProcessor.js#L131-L146 (and uses async, just like you are) – Evan Jan 11 '15 at 22:15
  • No each action has something like {preProcessors : ['pre_middleware'], postProcessors : ['post_middleware']} and it only grabs those ones from the list and execute them in order. in this case I have two main processors which one takes care of pres and the other one, posts: api.actions.addPreProcessor(preMiddlewareProcessor); api.actions.addPostProcessor(postMiddlewareProcessor); btw this is not happening in this code, but it's how I changed it and is working pretty good !! – DanialK Jan 12 '15 at 04:50