After hearing great points from the community, and experiencing the need for this in some of our projects, all but the bare bones Sails core is going to be pulled into plugins. The basic plugins will all be installed by default in new projects, but now it's possible to disable the things you don't want. It also makes it much easier to customize Sails for your needs, and probably enables all sorts of cool things none of us have even considered yet. ** More on this to come **
- REST blueprints will now be applied using the
controllers
hook, and configurable-- on by default.- Automatic controller routing (add a middleware method to a controller and you can hit it at
/controller/middleware
) will also be applied via thecontrollers
hook, on by default, and is now considered part of the blueprints (the other blueprints just happen only to work if you have a model, since they need something to get allllll RESTful on.)- csrf is controlled via
sails.config.controllers.csrf
as part of thecontrollers
hook,false
by default.- i18n is controlled via
sails.config.views.i18n
as part of theviews
hook,true
by default.
- The only thing better than thin controllers is no controllers.
- Middleware is good- it's nice to do a little HTTP traffic control in a more declarative way, and can dramatically simplify your workflow as you extend the application. historically, we've called them policies. The last middleware is your controller.
- Architecture must allow for dynamic rebuilding of the router at runtime in development mode, and router should get out of the way in production and pipe requests directly into Express's and Socket.io's routers whenever possible to maximize performance. In general, if you don't ever want a piece of code to be accesible via the server, don't put it in a controller. Instead, create a service or add the logic as a method for one of your models.
- Routes can now specify a list of targets, which will be run in order (this allows for binding chains of middleware directly to routes). Both controllers (
controller.action
) and arbitrary middleare (middleware
) functions from the new, optionalmiddleware
directory can be specified, in any order:
{
'post /signup': ['user.unique', 'user.create', 'verifyemail.send'],
'/auth/logout': ['authenticated', 'auth.logout']
}
You can also still use classic {controller: 'foo', action: 'bar' }
notation.
And conveniently, miscellaneous strings are treated as redirects:
{
// Alias '/logout' to '/auth/logout'
'get /logout': '/auth/logout',
'/thegoogle': 'http://google.com'
}
- Legacy policy config still works-- if
config/policies.js
is specified, the provided policies will be mapped to controllers/actions, vs. the new routing options which allow multiple actions and policies to be intermingled as arbitrary middleware bound at the route level. Both concepts can ceoexist- but the policies.js config file won't appear in new projects by default. - Methods in controllers like
ControllerName['get foo']()
now automatically routed toget /controllername/foo
instead ofget /controllername/get%20foo
For example, if you have aDogController
and you add the methodput index
, a route will be generated:put /dog
. Note that this will override the blueprint's RESTful update() method.
-
sails.config.csrf
Defaults tofalse
. If truthy, all non-GET requests require a special CSRF id to prove that the request is originating from an acceptable origin (prevents session hijacking attack). Under the covers, the hook uses the Express csrf middleware to set a_csrf
available in your server-side views.If you're creating a single-page web app, a phonegap app, a chrome extension, or otherwise can't get a hold of the _csrf token since you're never getting a rendered server-side view, the csrf hook automatically binds a custom route just for you. If set to
csrf: true
, the default route ofget /csrfToken
is created, which responds with a JSON object:{ "_csrf":"blahblah" }
. Insteading of setting the config to true, you can also specify a route, in which case the csrf middleware will be served on that route instead. Keep in mind it needs to be a GET requests will work, since the whole point of this is to prevent other types of requests until the user has a valid token.
sails.config.controller.blueprints.routes
is an object containing all of the automatically generated blueprint routes, allowing you to cherry pick or override the blueprints you want enabled in your app. By default, the default route patterns are all set totrue
, which indicates that the defaults should be use. If you set one of the routes tofalse
, it will be disabled globally. If you set it to a custom middleware function, that function will be used instaed. Note that this isn't just for the blueprint REST API-- this is also where the automatic routing takes place that allows you to add a method to a controller and have a route automatically generated. The RESTful routes (create
,destroy
,find
, etc.) require that a corresponding model exist (e.g. in order for RESTful blueprints to do anything with/user
, you need both aUserController.js
and aUser.js
model.) This is a global setting that applies to blueprints in all of your controllers.
sails.config.controllers.blueprints.explicitIntegerId
is set totrue
by default. When enabled, only natural numbers will match the:id
route parameter. You'd want to disable this if you want to lookup models by mongo id, or a custom, non-integer primary key blueprints. This is a global setting that applies to blueprints in all of your controllers.
sails.config.controllers.blueprints.prefix
is set to''
by default. If you set this key, all blueprint routes will be set up using the route prefix you specify. e.g. if prefix is/api/v2
, the blueprint routes for theDeliveryController
will be served asget /api/v2/delivery
,post /api/v2/delivery
,/api/v2/someMethodInYourController
, and so on. This is a global setting that applies to blueprints in all of your controllers.
Note: The configured policies in
config/policies.js
are applied in-order for their targeted controller or action wherever that controller or action is routed to. When configured in this way, they are completely separate from routes. This is convenient since you might like to access a controller in more than one way, and want it to be protected no matter what URL or HTTP method you access it with.
- Custom Express middleware (usually calls next(), and so requests are allowed to continue down the list below)
- Custom routes in
routes.js
config - Controller methods
- Automatically serve views that match the requested path structure
- CRUD blueprints
As always, if you're stuck, feel free to reach out to the Google Group or the #sailsjs IRC on freenode!
-Mike
Wotcha Mike,
I was thinking of the targets on routes last night & policy chains.
One of the problems we face is with admin routes - essentially you fold in a set of controller actions & policies under a namespace. For routing - this means 2 things:
To simplify this with the new approach - it would be helpful if we could use nested arrays in the policy-chain on routes.
Nested arrays would just be _.flatten() when the policy is compiled.
This would allow something like:
Basically allowing shorthand and a single point to be changed as the code evolves or the app changes.
The 2nd thing is socket routing - we sometimes use versioned namespaced sockets - this helps a lot in testing on the clientside, prototyping apps and mashups.
Clientside this looks like:
and then on the serverside - we can run and remap:
Any thoughts - with the work on routing - on how we can simplify sockety type work like this?
I know this is a bit of an edge case - but it's extremely helpful to mash up these things and some spiffy route style approach would be pretty handy.
Often routing acts as a translation layer|buffer between the frontend and the backend - esp in large apps.