I would like to completely redefine res.render method in sailsjs app:
my template engine is not compatible with consolidate.
I decided to use hook for it, but can't understand, how I can to write my own realization of res.render.
Any thoughts or suggestions?
Thanks!
You can do the same as here lib/hooks/views/index.js (in your sails hook)
sails.on('router:before', function() {
sails.router.bind('/*', function(req, res, next) {
// here you can reimplement the res.render method, of example:
res.render = function render(data) {
// For access `req` data in your template engine,
// many useful info stored here like:
// req.options.controller – current `controller`
// req.options.action – current `action`
data.req = req;
var html = yourTemplateEngineRender(data);
res.set({
'Content-Type': 'text/html',
'Content-Length': Buffer.byteLength(html, 'utf8')
});
res.send(html);
}
}, 'all');
});
See my hook which enables bem rendering for sails: sails-hook-bem-render
Related
I'm migrating a project to Sails.js, I decided use Sails because I need to chaing many functions to single path and in it's documentation sais it is posible but I tryed a single example and I can not make it works, when I try to execute two functions on a path i got this error:
Error: next (as in req,res,next) should never be called in an action function (but in action algo/fn1, it was!) It was called with no arguments. Please use a method like res.ok() or res.json() instead.
What am I doing wrong or how can I make it works?
this is my code:
routes.js
// ...
'get /chain': [
'AlgoController.fn1',
'AlgoController.fn2'
],
// ...
AlgoController.js
let Controller = {};
Controller.fn1 = function(req, res, next) {
req.executed = ['executed fn1'];
next();
};
Controller.fn2 = function(req, res, next) {
req.executed.push('executed fn2');
res.send(req.executed.join(' and '));
};
module.exports = Controller;
If I delete the next() or use res.ok() / res.json(), the second function is never executed.
Well, I solved this using req.next() instead next(), so this is the code:
let Controller = {};
Controller.fn1 = function(req, res) {
req.executed = ['executed fn1'];
return req.next(); // this is how you call next fn
};
Controller.fn2 = function(req, res) {
req.executed.push('executed fn2');
res.send(req.executed.join(' and '));
};
module.exports = Controller;
This works well, hope this help somebody else.
I'm in the process of building a new AngularJS frontend for a Drupal 7 website. This is using the Services module with session-based authentication, across two domains using CORS. I am able to authenticate with Drupal, retrieve the user object and session data, and then get the CSRF token from the services module. What I'm having trouble with is setting all this up in the header so that subsequent requests are authenticated. I understand the overall concept but am new to both AngularJS and preventing CSRF attacks.
From what I have gathered reading about this set-up with AngularJS and RubyOnRails, there can be inconsistencies between platforms concerning what the token is named and how it is processed. There also seems to be a number of suggestions on how to set this token in the header. However, I'm having trouble in finding a solid example of how to get these platforms speaking the same language.
The only thing I'm doing with my $httpProvider in app.js is:
delete $httpProvider.defaults.headers.common['X-Requested-With'];
The login controller, in controller.js:
.controller('LoginCtrl', ['$scope', '$http', '$cookies', 'SessionService', function($scope, $http, $cookies, SessionService) {
$scope.login = function(user) {
//set login url and variables
var url = 'http://mywebsite.com/service/default/user/login.json';
var postDataString = 'name=' + encodeURIComponent(user.username) + '&pass=' + encodeURIComponent(user.password);
$http({
method: 'POST',
url: url,
data : postDataString,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function (data, status, headers, config) {
var sessId = data.sessid;
var sessName = data.session_name;
$cookies[sessName] = sessId;
var xsrfUrl = 'http://mywebsite.com/services/session/token';
$http({
method: 'GET',
url: xsrfUrl
}).success(function (data, status, headers, config) {
$cookies["XSRF-TOKEN"] = data;
SessionService.setUserAuthenticated(true);
}).error(function (data, status, headers, config) {
console.log('error loading xsrf/csrf');
});
}).error(function (data, status, headers, config) {
if(data) {
console.log(data);
var msgText = data.join("\n");
alert(msgText);
} else {
alert('Unable to login');
}
});
};
The solution has to do with how the cookies need to be set and then passed through subsequent requests. Attempts to set them manually did not go well but the solution was simpler than I expected. Each $http call needs to set the options:
withCredentials: true
Another change I made was to use the term CSRF instead of XSRF, to be consistent with Drupal. I didn't use any built-in AngularJS CSRF functionality.
addItem: function(data)
{
return $http.post('api/programs/'+$stateParams.id+'/workouts', {item:data},{
headers:
{
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'X-CSRF-Token': $('meta[name="xxtkn"]').attr('content')
}
});
}
since it has been a year of this topic! not sure still encountering the same problem but for the ones who comes to search for answers here is how i handle it!
Pay attention the headers{} part i define a new header and call it X-CSRF-Token and grab value from the DOM of (serverside) generated html or php. It is not a good practise to also request the csrf token from the server.Cuz attacker could somehow request that as well. Since you save it as a cookie. Attacker can steal the cookie! No need to save it in a cookie! send the token with header and read it in the serverside to match it!
and for multitab of a same page issue. I use the same token thruout the whole session.
Only regenerate on login, logout and change of major site or user settings.
There is a great library callse ng-drupal-7-services. If you use this in you project it solves authentication / reauthentication and file / node creation aut of the box and you can fokuse on the importent stuff in your project.
So Authentication is there solved like this:
function login(loginData) {
//UserResource ahndles all requeste of the services 3.x user resource.
return UserResource
.login(loginData)
.success(function (responseData, status, headers, config) {
setAuthenticationHeaders(responseData.token);
setLastConnectTime(Date.now());
setConnectionState((responseData.user.uid === 0)?false:true)
setCookies(responseData.sessid, responseData.session_name);
setCurrentUser(responseData.user);
AuthenticationChannel.pubLoginConfirmed(responseData);
})
.error(function (responseError, status, headers, config) {
AuthenticationChannel.pubLoginFailed(responseError);
});
};
(function() {
'use strict';
AuthenticationHttpInterceptor.$inject = [ '$injector'];
function AuthenticationHttpInterceptor($injector) {
var intercepter = {
request : doRequestCongiguration,
};
return intercepter;
function doRequestCongiguration (config) {
var tokenHeaders = null;
// Need to manually retrieve dependencies with $injector.invoke
// because Authentication depends on $http, which doesn't exist during the
// configuration phase (when we are setting up interceptors).
// Using $injector.invoke ensures that we are provided with the
// dependencies after they have been created.
$injector.invoke(['AuthenticationService', function (AuthenticationService) {
tokenHeaders = AuthenticationService.getAuthenticationHeaders();
}]);
//add headers_______________________
//add Authorisation and X-CSRF-TOKEN if given
if (tokenHeaders) {
angular.extend(config.headers, tokenHeaders);
}
//add flags_________________________________________________
//add withCredentials to every request
//needed because we send cookies in our request headers
config.withCredentials = true;
return config;
};
There is also some kind of kitchen sink for this project here: Drupal-API-Explorer
Yes, each platform has their own convention in naming their tokens.
Here is a small lib put together hoping to make it easy to use with different platforms. This will allow you to use set names and could be used across all requests. It also works for cross-domain requests.
https://github.com/pasupulaphani/angular-csrf-cross-domain
I'd like to define a module which computes a new dependancy, fetches it and then returns the result. Like so:
define(['defaults', 'get_config_name'], function(defaults, get_config_name) {
var name = get_config_name();
var config;
require.synchronous([configs / '+name'], function(a) {
config = defaults.extend(a);
});
return config;
});
Is there a way to do this or a better way to attack this problem?
You may try to use synchronous RequireJS call require('configs/'+get_config_name()), but it will load a module synchronously only if it is already loaded, otherwise it will throw an exception. Loading module/JavaScript file synchronously is technically impossible.
UPD: It's possible (see Henrique's answer) but highly unrecommended. It blocks JavaScript execution that causes to freezing of the entire page. So, RequireJS doesn't support it.
From your use case it seems that you don't need synchronous RequireJS, you need to return result asynchronously.
AMD pattern allows to define dependencies and load them asynchronously, but module's factory function must return result synchronously. The solution may be in using loader plugin (details here and here):
// config_loader.js
define(['defaults', 'get_config_name'], function(defaults, get_config_name) {
return {
load: function (resourceId, require, load) {
var config_name = 'configs/' + get_config_name();
require([config_name], function(config) {
load(defaults.extend(config));
})
}
}
});
// application.js
define(['config_loader!'], function(config) {
// code using config
});
If get_config_name() contains simple logic and doesn't depend on another modules, the better and simpler is calculating on the fly paths configuration option, or in case your config depends on context - map configuration option.
function get_config_name() {
// do something
}
require.config({
paths: {
'config': 'configs/' + get_config_name()
}
});
require(['application', 'defaults', 'config'], function(application, defaults, config) {
config = defaults.extend(config);
application.start(config);
});
Loading JavaScript synchronously is NOT technically impossible.
function loadJS(file){
var js = $.ajax({ type: "GET", url: file, async: false }).responseText; //No need to append
}
console.log('Test is loading...');
loadJS('test.js');
console.log('Test was loaded:', window.loadedModule); //loadedModule come from test.js
I am building a small app primarily with socket io, however with a few things from expressjs.
One function of the socket io piece is to send an email when a certain event occurs. I've got this working fine with node_mailer.
The problem I'm running into is that I want to use the express view engine to render the emails from template files. The render method seems to be explicitly attached to the res object prototype.
What I've done feels pretty dirty:
// setup express server
var render;
app.get('/', function (req, res) {
if (typeof render == 'undefined') render = res.render;
res.end('Welcome to app');
});
// socket io code
socket.on('event', function (data) {
var email_content;
render('template', {}, function (err, result) { email_content = result; });
});
Is there a better way to gain access to expressjs's components outside the context of an http request, or even a better way to approach this problem? I tried rigging up a call to the exported express.view.compile function but that both didn't work and seemed like a high hoo
Here is where the information you seek comes from:
https://github.com/Ravelsoft/node-jinjs/wiki
With templates as modules
To have node load your templates as if they were modules, you first have to register your module extension :
require("jinjs").registerExtension(".tpl");
If you want your file to be transformed prior to being submitted to jinjs, you can pass a callback ;
var pwilang = require("pwilang");
require("jinjs").registerExtension(".pwx", function (txt) {
return pwilang.parse(txt);
});
You can now write this to user Jin:
var my_template = require("./mytemplate");
var context = { foo: "foo", bar: "bar" };
var result = my_template.render(context);
Because you are sticking Jin into express (as opposed to making express work with Jin) this is your best option. The res variable is only available in the route callback.
On express 3.x there is the alias app.render
// socket io code
socket.on('event', function (data) {
var email_content;
app.render('template', {}, function (err, result) { email_content = result; });
});
I'm using "express" and "cradle" in "nodejs". If I request my database I have to define a callback to handle the response. Unfortunately I have no access to res (response) in my callback function. What is the best practice for this problem? Here is my code.
var cradle = require('cradle');
var db = new cradle.Connection().database('guestbook');
app.get('/guestbook', function(req, res) {
db.view('guestbook/all', function(err, doc) {
console.log(doc);
// How can I use res in this callback
// to send the response?
});
});
You can just use res inside the inner callback.
In JavaScript the inner function "inherits" the variables of the outer function. Or more precisely, the function forms a closure, which is an expression that can have free variables. The closure binds the variables from its outer scope, which can be the scope of another function or the global scope.
You may try this.
Most important (perhaps your pitfall?) keep in mind that 'db.view' will mereley register a callback closure and continue. Do not close your request (by calling 'req.end') anywhere outside this closure. If you do, quite likely the request have been closed as the db returns. Once the http response object is closed any data written to it goes void.
var cradle = require('cradle');
var db = new cradle.Connection().database('guestbook');
app.get('/guestbook', function(req, res) {
// Register callback and continue..
db.view('guestbook/all', function(err, guests) {
// console.log('The waiting had an end.. here are the results');
guests.forEach(function(guest) {
if (guest.name) {
res.write('Guest N: ' + guest.name);
}
});
// Close http response (after this no more output is possible).
res.end('That is all!')
});
console.log('Waiting for couch to return guests..');
// res.end('That is all!'); // DO NOT DO THIS!!!
});
With this snippet you really should have access to res here. You should be able to use res.render() or res.send() because the db callback is wrapped in the closure of the app.get callback function.