Technically, much of the code you’ll write in a Sails app is middleware, in that runs in between the incoming request and the outgoing response—that is, in the "middle" of the request/response stack. In an MVC framework, the term “middleware” typically refers more specifically to code that runs before or after your route-handling code (i.e. your controller actions), making it possible to apply the same piece of code to multiple routes or actions. Sails has robust support for the middleware design pattern. Depending on your needs, you may choose to implement:
routes
feature implemented—to apply code before one or more route handlersSails is fully compatible with Express / Connect middleware, which are functions that accept req
, res
and next
as arguments. Every app utilizes a configurable middleware stack for handling HTTP requests. Each time the app receives an HTTP request, its configured HTTP middleware stack runs in order.
Note that this HTTP middleware stack is only used for "true" HTTP requests; it is ignored for virtual requests (e.g. requests from a live Socket.io connection).
By default, Sails uses a few different middleware functions to handle low-level HTTP-related tasks. These are things like interpreting cookies, parsing HTTP request bodies, serving assets, and even attaching your app's routes. You can read more about the default middleware stack here.
Since the middleware stack comes with reasonable defaults, many Sails apps won't need to modify this configuration at all. But for situations where you need more flexibility, Sails makes it simple to add, reorder, override, and disable the functions in your app's HTTP middleware stack.
To configure a new custom HTTP middleware function, add a middleware function as a new key in middleware
(e.g. "foobar"), then add the name of its key ("foobar") in the middleware.order
array, wherever you'd like it to run in the middleware chain.
With the exception of "order", which is reserved for configuring the order of the middleware stack, any value assigned to a key of sails.config.middleware
should be a function which takes three arguments: req
, res
and next
. This function works almost exactly like a policy, the only visible difference is when it's executed.
If you need to run some one-time set up code for a custom middleware function, you'll need to do so before passing it in. The recommended way of doing this is with a self-calling (i.e. "immediately-invoked") wrapper function. In the example below, note that rather than setting the value to a "req, res, next" function directly, a self-calling function is used to "wrap" some initial setup code. That self-calling wrapper function then returns the final middleware (req,res,next) function, so it gets set on the key just the same was as if it had been passed in directly.
The following example shows how you might set up three different custom HTTP middleware functions:
// config/http.js
module.exports.http = {
middleware: {
order: [
'cookieParser',
'session',
'passportInit', // <==== If you're using "passport", you'll want to have its two
'passportSession', // <==== middleware functions run after "session".
'bodyParser',
'compress',
'foobar', // <==== We can put other, custom HTTP middleware like this wherever we want.
'poweredBy',
'router',
'www',
'favicon',
],
// An example of a custom HTTP middleware function:
foobar: (function (){
console.log('Initializing `foobar` (HTTP middleware)...');
return function (req,res,next) {
console.log('Received HTTP request: '+req.method+' '+req.path);
return next();
};
})(),
// An example of a couple of 3rd-party HTTP middleware functions:
// (notice that this time we're using an existing middleware library from npm)
passportInit : (function (){
var passport = require('passport');
var reqResNextFn = passport.initialize();
return reqResNextFn;
})(),
passportSession : (function (){
var passport = require('passport');
var reqResNextFn = passport.session();
return reqResNextFn;
})()
},
}
You can also use the strategy described above to override built-in middleware like the body parser (see Customizing the body parser).
While this is not recommended, you can even disable a built-in HTTP middleware function entirely—just remove it from the
middleware.order
array. This allows for complete flexibility, but it should be used with care. If you choose to disable a piece of built-in middleware, make sure you fully understand the consequences. Disabling built-in HTTP middleware may dramatically change the way your app works.
One of the really nice things about Sails apps is that they can take advantage of the wealth of existing Express/Connect middleware, but a common question arises when people actually try to do this:
"Where do I
app.use()
this thing?".
In most cases, the answer is to install the Express middleware as a custom HTTP middleware in sails.config.http.middleware
. This will trigger it for all HTTP requests to your Sails app, and allow you to configure the order in which it runs in relation to other HTTP middleware.
You should never override or remove the
router
HTTP middleware. It is built-in to Sails; without it, your app's explicit routes and blueprint routes will not work.
To make Express middleware apply to only a particular action, you can also include Express middleware as a policy—just be sure that you actually want it to run for both HTTP and virtual socket requests.
To do this, edit config/policies.js
to either require and setup the middleware in an actual wrapper policy (usually a good idea) or to require it directly in your policies.js file. The following example uses the latter strategy for brevity:
var auth = require('http-auth');
var basic = auth.basic({
realm: 'admin area'
}, function (username, password, onwards) {
return onwards(username === 'Tina' && password === 'Bullock');
});
//...
module.exports.policies = {
'*': [true],
// Prevent end users from doing CRUD operations on products reserved for admins
// (uses HTTP basic auth)
'product/*': [auth.connect(basic)],
// Everyone can view product pages
'product/show': [true]
}