streaming view rendering
Allows for a view engine to return a stream upon being asked to render a path. This allows for the engine to simply stream out data as it is loaded from disk (or other IO location) without having to collect it all first and then send it. Fallback mode for all existing engines that call `fn` upon completion takes over if no stream is returned. This works for both app.render and res.render
Esse commit está contido em:
+20
-1
@@ -501,7 +501,26 @@ app.render = function(name, options, fn){
|
||||
|
||||
// render
|
||||
try {
|
||||
view.render(opts, fn);
|
||||
var view_stream = view.render(opts, fn);
|
||||
|
||||
// if the engine returned a stream AND user specified a function
|
||||
// then we will capture data for user
|
||||
if (view_stream && fn) {
|
||||
var body = '';
|
||||
view_stream.on('data', function(chunk) {
|
||||
body += chunk;
|
||||
});
|
||||
|
||||
view_stream.once('error', function(err) {
|
||||
fn(err);
|
||||
});
|
||||
|
||||
view_stream.once('end', function() {
|
||||
fn(null, body);
|
||||
});
|
||||
}
|
||||
|
||||
return view_stream;
|
||||
} catch (err) {
|
||||
fn(err);
|
||||
}
|
||||
|
||||
+41
-4
@@ -792,12 +792,49 @@ res.render = function(view, options, fn){
|
||||
// merge res.locals
|
||||
options._locals = self.locals;
|
||||
|
||||
// default callback to respond
|
||||
fn = fn || function(err, str){
|
||||
// support for non streaming view rendering
|
||||
// if renderer uses streams, this will not be called
|
||||
// see below for how streams are handled
|
||||
var respond = fn || function(err, str){
|
||||
if (view_stream) return;
|
||||
|
||||
if (err) return req.next(err);
|
||||
self.send(str);
|
||||
};
|
||||
|
||||
// render
|
||||
app.render(view, options, fn);
|
||||
var view_stream = app.render(view, options, respond);
|
||||
|
||||
// old style, will have already called respond for us
|
||||
if (!view_stream) {
|
||||
return;
|
||||
}
|
||||
|
||||
// user wants to handle sending themselves
|
||||
if (fn) {
|
||||
var body = '';
|
||||
view_stream.on('data', function(chunk) {
|
||||
body += chunk;
|
||||
});
|
||||
|
||||
view_stream.once('error', function(err) {
|
||||
fn(err);
|
||||
});
|
||||
|
||||
view_stream.once('end', function() {
|
||||
fn(null, body);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// set response headers
|
||||
self.statusCode = 200;
|
||||
if (!self.get('Content-Type')) {
|
||||
self.charset = self.charset || 'utf-8';
|
||||
self.type('html');
|
||||
}
|
||||
self.setHeader('Transfer-Encoding', 'chunked');
|
||||
|
||||
// start streaming the response
|
||||
view_stream.pipe(self);
|
||||
};
|
||||
|
||||
+1
-1
@@ -73,5 +73,5 @@ View.prototype.lookup = function(path){
|
||||
*/
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
this.engine(this.path, options, fn);
|
||||
return this.engine(this.path, options, fn);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('./support/http')
|
||||
, fs = require('fs');
|
||||
|
||||
function render(path, options, fn) {
|
||||
@@ -10,6 +11,10 @@ function render(path, options, fn) {
|
||||
});
|
||||
}
|
||||
|
||||
function streaming_render(path, options, fn) {
|
||||
return fs.createReadStream(path, { encoding: 'utf-8' });
|
||||
}
|
||||
|
||||
describe('app', function(){
|
||||
describe('.engine(ext, fn)', function(){
|
||||
it('should map a template engine', function(done){
|
||||
@@ -76,5 +81,42 @@ describe('app', function(){
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should support streaming engines', function(done) {
|
||||
var app = express();
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.engine('.html', streaming_render);
|
||||
app.set('view engine', '.html');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
// using function should just call the function when done
|
||||
app.render('user', function(err, str){
|
||||
if (err) return done(err);
|
||||
str.should.equal('<p>{{user.name}}</p>');
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('should render a response using the streaming engine', function(done) {
|
||||
var app = express();
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.engine('.html', streaming_render);
|
||||
app.set('view engine', '.html');
|
||||
app.locals.user = { name: 'tobi' };
|
||||
|
||||
app.get('/', function(req, res) {
|
||||
res.render('user');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.end(function(err, res) {
|
||||
if (err) return done(err);
|
||||
res.header['transfer-encoding'].should.equal('chunked');
|
||||
res.text.should.equal('<p>{{user.name}}</p>');
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário