-
Notifications
You must be signed in to change notification settings - Fork 0
Controller
Each request creates a new instance of your controller. Your controller should always extend the GladController.
/**
* ReviewController is a controller for the Review resource.
*
* @module controllers/review
* @version 0.0.1
*/
const {imports} = Glad;
const Review = imports("reviewModel");
class ReviewController extends Glad.Controller {
Get () {
Review.find()
.then(reviews => this.res.json(reviews))
.catch(err => this.error(err));
}
FindOne () {
Review.findOne({ _id: this.params.id})
.then(review => this.res.json(review))
.catch(err => this.error(err));
}
Post () {
new Review(this.body)
.then(review => this.res.status(201).json(review))
.catch(err => this.error(err));
}
Put () {
Review.findOneAndUpdate({_id: this.params.id}, this.body)
.then(review => this.res.json(review))
.catch(err => this.error(err));
}
Delete () {
Review.findOne({_id: this.params.id}).remove()
.then(() => this.res.status(204).send())
.catch(err => this.error(err));
}
}
module.exports = ReviewController;
Methods / Objects available using this from within an action.
| method / object | Short Description |
|---|---|
| cache | Caching class |
| actionCache | Action caching store |
| cacheStore | Current request's cache store |
| req | Express request object |
| res | Express response object |
| params | Alias for this.req.params
|
| body | Alias for this.req.body
|
| query | Alias for this.req.query
|
| redisClient | Redis Connection |
| permit | A method that removes non-whitelisted objects. Nested Objects are not allowed without using dot notation to specify the nested keys. |
| deepPermit | A method that removes non-whitelisted objects. Nested Objects are allowed. |
| permitted | Boolean, has this action called permit. This may be useful to enforce permitting. |
| render | Renders a view. |
| socketIO | The initialized socketIO library |
Actions can be cached in two ways. Using the callback method or the chainable method.
The callback method
When using the callback method you tell the controller that you want to cache the result of this method for next time.
You will provide some configuration regarding what type of strategy you'd like to use, as well as the max number of items that this particular
method is allowed to store in cache. This allows you to better understand how much data you'll be putting into your caches, and allows you to
favor certain methods over other (maybe less important) methods. The available named strategies are "LRU" and "LFU" for controller actions.
If you would prefer to implement your own strategy, you can specify that by providing a additional parameters called score and increment. For LRU cache the scoring function is simply () => new Date().getTime() with increment : false. For LFU it is () => 1 with increment : true.
Now to the callback. Below you can see that we are sending in a callback that contains our code to generate the data we need to send back to the client. It will receive a caching function that you should call once your final data is generated. The first time this request happens, it runs your code to generate the response. The next time, it will not run the callback. Instead it will get the data from redis.
GET () {
this.cache({ max: 200, strategy: 'LFU' }, cache => {
SomeModel.find()
.limit(15)
.then(users => this.res.json(users) && cache(users))
.catch(err => this.error(err))
});
}It is worth noting that a cache hit is defined by the url including any query parameters. So '/api/widgets?foo=1&bar=2' !== /api/widgets?foo=1.
Just like the callback method, the code that runs to generate the data you need to send back to the client only gets executed on a cache:miss. In this case, it's a callback sent into the miss method. You also have an optional hit method that receives the cached data so that you can determine how you'd like to respond. (This could be useful when you'll be rendering a view with the data)
this.cache({ max: 100, strategy: 'LRU' })
.miss(cache => {
Products.find({category: 'widgets'}).exec( (err, widgets) => {
this.res.status(200).json(widgets);
cache(widgets);
});
})
.hit(data => this.res.status(200).json(data))
.exec();What if i'm not using json? this.cache({ max: 2, strategy: 'LFU', type: 'html' }) you can pass in an additional parameter called type. This can be any type that express understands.
The actionCache method retrieves the cache store used for a specific method on your controller. Since controller methods have their own separate namespaced cache, you will need to look up an action's cache namespace if you want to operate on it from a different action. This is especially useful if you need to drop/re-warm a cache when data changes. As an example, let's say that you have a controller setup for widgets, and when you HTTP PUT a widget, you want to remove that item from the cache. Let's also assume that FindOne handles the GET requests for a specific widget, and the GET method handles GET requests for an array of widgets.
From the PUT action on the controller
this.actionCache('FindOne').del('/widgets/12').then(..do stuff);
this.actionCache('GET').del('/widgets').then(..do stuff);The cacheStore object is an instance of ControllerCache delegated to the current controller instance.
This means that this.cacheStore only operates on the current action's cache namespace.
API: All methods return a Promise.
| method | Description |
|---|---|
set(key, value, maxAge) |
set value for the given key, marking it as the most recently accessed one. Keys should be strings, values will be - - - JSON.stringified. The optional maxAge overrides for this specific key the global expiration of the cache. |
get(key) |
resolve to the value stored in the cache for the given key or null if not present. If present, the key will be marked as the most recently accessed one. |
getOrSet(key, fn, maxAge) |
resolve to the value stored in the cache for the given key. If not present, execute fn, save the result in the cache and return it. fn should be a no args function that returns a value or a promise. If maxAge is passed, it will be used only if the key is not already in the cache. |
peek(key) |
resolve to the value stored in the cache for the given key, without changing its last accessed time. |
del(key) |
removes the item from the cache. |
reset() |
empties the cache. |
has(key) |
resolves to true if the given key is present in the cache. |
keys() |
resolves to an array of keys in the cache, sorted from most to least recently accessed. |
values() |
resolves to an array of values in the cache, sorted from most to least recently accessed. |
count() |
resolves to the number of items currently in the cache. |
See Express Request
See Express Response
See req.params
See req.query
See req.body
Initialized redis client. See redis package on NPM
Whitelist allowable data in a request body. This is a good idea if you are mass assigning things.
Ex: Shallow
this.req.body = { name: 'Fooze', admin: true };
this.permit('name', 'email', 'phone');
// this.req.body === { name: 'Fooze' }Ex: Deep
this.req.body = { user: { name: 'Fooze', admin: true } };
this.permit('user.name', 'user.email', 'user.phone');
// this.req.body === { user: { name: 'Fooze' } }- You must be specific when the key contains an object.
- You cannot permit the whole user object at once. In order to permit "sub documents" you need to use the deepPermit method instead. This is intentional because it can defeat the purpose of permit when you permit a subdocument that could potentially contain things that shouldn't be allowed.
After calling permit, this.permitted will be true.
The render method is an enhanced version of Express' res.render. Calling this.render from a controller automatically looks in the correct folder for the view file. As an example, let's say you have a controller for a resource called widgets. This means that there should be a folder in the views directory called widgets. Invoking this.render('my-widget', {data: 'stuff'}) will render the view located at views/widgets/my-widget.pug.