I want to send a html file and an object to the client from server using express such that when the file gets loaded it uses the object and structure dynamically using ajax.
I know that the html file can be send like this:
res.sendFile( __dirname + "/" + "main.html" )
object as:
res.json(obj);
But how to send them together?
In simple word, you can not send json and html together as we need to send content-type in header. You can either send html or json.
Another way, you can send html into josn with other object something like following
const fs = require('fs');
const html = fs.readFileSync( __dirname + '/main.html' );
res.json({html: html.toString(), data: obj});
There are a couple of ways you can do that. While not the best method, this is the one I used in my projects. I used ejs, a powerful and simple templating engine.
First, install it using npm
npm install ejs
Then, in your HTML add:
<html>
<body>
<script type="text/javascript">
var obj = JSON.parse(<%= objSentFromServer %>)
// do something with obj
</script>
</body>
</html>
Server Side:
let express = require('express')
let app = express()
let ejs = require('ejs')
let fs = require('fs')
let objectSentFromServer = ... // what you need to send
app.get('/', (req, res) => {
fs.readFile(__dirname + '/main.html', (err, html) => {
res.send(ejs.render(html, JSON.stringify(objectSentFromServer)))
})
})
app.listen(8080, (err) => { console.log(err) })
Of course, there are plenty of other ways.
You may not be able to achieve this by sending back plain HTML.
But you should be able to achieve this using a templating engine. With the help of templating engine, you should be able to render a view as well as pass JSON required to used in that template.
Reference: render view
Eg;
// pass a local variable to the view
res.render('user', { name: 'Tobi' }, function(err, html) {
// ...
});
Here is the complete list of Template Engines supported by Express JS
Related
I'm having trouble with a variable (config) declared in a jade template file (index.jade) that isn't passed to a javascript file, which then makes my javascript crash. Here is the file (views/index.jade):
h1 #{title}
script(src='./socket.io/socket.io.js')
script(type='text/javascript')
var config = {};
config.address = '#{address}';
config.port = '#{port}';
script(src='./javascripts/app.js')
Here is a part of my app.js (server side):
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
app.use(express.static(__dirname + '/public'));
});
app.configure('development', function(){
app.set('address', 'localhost');
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(express.errorHandler());
});
// Routes
app.get('/', function(req, res){
res.render('index', {
address: app.settings.address,
port: app.settings.port
});
});
if (!module.parent) {
app.listen(app.settings.port);
console.log("Server listening on port %d",
app.settings.port);
}
// Start my Socket.io app and pass in the socket
require('./socketapp').start(io.listen(app));
And here is a part of my javascript file that crashes (public/javascripts/app.js):
(function() {
var socket = new io.Socket(config.address, {port: config.port, rememberTransport: false});
I'm running the site on development mode (NODE_ENV=development) on localhost (my own machine). I'm using node-inspector for debugging, which told me that the config variable is undefined in public/javascripts/app.js.
Any ideas?? Thanks!!
It's a little late but...
script.
loginName="#{login}";
This is working fine in my script. In Express, I am doing this:
exports.index = function(req, res){
res.render( 'index', { layout:false, login: req.session.login } );
};
I guess the latest jade is different?
Merc.
edit: added "." after script to prevent Jade warning.
#{} is for escaped string interpolation which automatically escapes the input and is thus more suitable for plain strings rather than JS objects:
script var data = #{JSON.stringify(data)}
<script>var data = {"foo":"bar"} </script>
!{} is for unescaped code interpolation, which is more suitable for objects:
script var data = !{JSON.stringify(data)}
<script>var data = {"foo":"bar"} </script>
CAUTION: Unescaped code can be dangerous. You must be sure to sanitize any user inputs to avoid cross-site scripting (XSS).
E.g.:
{ foo: 'bar </script><script> alert("xss") //' }
will become:
<script>var data = {"foo":"bar </script><script> alert("xss") //"}</script>
Possible solution: Use .replace(/<\//g, '<\\/')
script var data = !{JSON.stringify(data).replace(/<\//g, '<\\/')}
<script>var data = {"foo":"bar<\/script><script>alert(\"xss\")//"}</script>
The idea is to prevent the attacker to:
Break out of the variable: JSON.stringify escapes the quotes
Break out of the script tag: if the variable contents (which you might not be able to control if comes from the database for ex.) has a </script> string, the replace statement will take care of it
https://github.com/pugjs/pug/blob/355d3dae/examples/dynamicscript.pug
In my case, I was attempting to pass an object into a template via an express route (akin to OPs setup). Then I wanted to pass that object into a function I was calling via a script tag in a pug template. Though lagginreflex's answer got me close, I ended up with the following:
script.
var data = JSON.parse('!{JSON.stringify(routeObj)}');
funcName(data)
This ensured the object was passed in as expected, rather than needing to deserialise in the function. Also, the other answers seemed to work fine with primitives, but when arrays etc. were passed along with the object they were parsed as string values.
If you're like me and you use this method of passing variables a lot, here's a write-less-code solution.
In your node.js route, pass the variables in an object called window, like this:
router.get('/', function (req, res, next) {
res.render('index', {
window: {
instance: instance
}
});
});
Then in your pug/jade layout file (just before the block content), you get them out like this:
if window
each object, key in window
script.
window.!{key} = !{JSON.stringify(object)};
As my layout.pug file gets loaded with each pug file, I don't need to 'import' my variables over and over.
This way all variables/objects passed to window 'magically' end up in the real window object of your browser where you can use them in Reactjs, Angular, ... or vanilla javascript.
See this question: JADE + EXPRESS: Iterating over object in inline JS code (client-side)?
I'm having the same problem. Jade does not pass local variables in (or do any templating at all) to javascript scripts, it simply passes the entire block in as literal text. If you use the local variables 'address' and 'port' in your Jade file above the script tag they should show up.
Possible solutions are listed in the question I linked to above, but you can either:
- pass every line in as unescaped text (!= at the beginning of every line), and simply put "-" before every line of javascript that uses a local variable, or:
- Pass variables in through a dom element and access through JQuery (ugly)
Is there no better way? It seems the creators of Jade do not want multiline javascript support, as shown by this thread in GitHub: https://github.com/visionmedia/jade/pull/405
Here's how I addressed this (using a MEAN derivative)
My variables:
{
NODE_ENV : development,
...
ui_varables {
var1: one,
var2: two
}
}
First I had to make sure that the necessary config variables were being passed. MEAN uses the node nconf package, and by default is set up to limit which variables get passed from the environment. I had to remedy that:
config/config.js:
original:
nconf.argv()
.env(['PORT', 'NODE_ENV', 'FORCE_DB_SYNC'] ) // Load only these environment variables
.defaults({
store: {
NODE_ENV: 'development'
}
});
after modifications:
nconf.argv()
.env('__') // Load ALL environment variables
// double-underscore replaces : as a way to denote hierarchy
.defaults({
store: {
NODE_ENV: 'development'
}
});
Now I can set my variables like this:
export ui_varables__var1=first-value
export ui_varables__var2=second-value
Note: I reset the "heirarchy indicator" to "__" (double underscore) because its default was ":", which makes variables more difficult to set from bash. See another post on this thread.
Now the jade part:
Next the values need to be rendered, so that javascript can pick them up on the client side. A straightforward way to write these values to the index file. Because this is a one-page app (angular), this page is always loaded first. I think ideally this should be a javascript include file (just to keep things clean), but this is good for a demo.
app/controllers/index.js:
'use strict';
var config = require('../../config/config');
exports.render = function(req, res) {
res.render('index', {
user: req.user ? JSON.stringify(req.user) : "null",
//new lines follow:
config_defaults : {
ui_defaults: JSON.stringify(config.configwriter_ui).replace(/<\//g, '<\\/') //NOTE: the replace is xss prevention
}
});
};
app/views/index.jade:
extends layouts/default
block content
section(ui-view)
script(type="text/javascript").
window.user = !{user};
//new line here
defaults = !{config_defaults.ui_defaults};
In my rendered html, this gives me a nice little script:
<script type="text/javascript">
window.user = null;
defaults = {"var1":"first-value","var2:"second-value"};
</script>
From this point it's easy for angular to utilize the code.
I am building a simple web app that takes in 3 different types of inputs and renders one of 3 different EJS file from a views directory (using Node and Express). Each of the rendered EJS files needs the contents of a static array. The static array is big and I want to store it in a separate JS file. I tried putting this code in the EJS files:
app.js
var arrayFile = require('./arrayFile.js');
app.get("/", async function(req, res){
let blockVariable = await request({
method: "get",
uri: "https://testardor.jelurida.com/nxt?requestType=getBlocks",
json: true
});
var blocks2 = []
blockVariable.blocks.forEach(function(element){
if (element.transactions.length != 0){
blocks2.push(element);
}
});
res.render("home", {blocks: blockVariable.blocks,
blocks2: blocks2,
arrayFile: arrayFile
});
});
home.ejs
<%= arrayFile %>
arrayFile.js
array = [{thing1: 1}, {thing2: 2}]
This gives results [object Object] on the home.ejs page
I tried making home.ejs
<%= JSON.stringify(arrayFile) %>
with results {}
I think you are mixing contexts. <script src="arrayFile.js"></script> will work on browser side. While <%= array %> works on server side. So it should be something like this, just to give you idea:
// assuming that your array is exported as default
var array = require('./arrayFile.js');
app.get('/page', (req, res) => {
// make array available to EJS context
res.render('page_ejs', {array});
});
But this will probably generate one big HTML with imprinted array. Which is not what you probably may want, because then you don't actually need <script src="arrayFile.js"></script> in your EJS.
If you want to files to be shipped to browser, your HTML and arrayFile.js, then you will have to properly export array in arrayFile.js so that you can write something like:
// this will be fetched by browser
<script src="arrayFile.js"></script>
// and then executed by browser
<script>
console.log(yourArray);
</script>
Then you don't need <%= array %> in your EJS.
Of course, you can mix both ways, making array available to EJS, and also to the browser.
Let's say I send to a Node Server a request to get a JS file:
<script src="/blabla.js" id="545234677">
and on my Node side:
app.get('/blabla.js', function(req, res, next) {
console.log(req.params.id); //undefined
res.setHeader('content-type', 'text/javascript');
res.render('blabla'); //default
});
and finally I have this template file blabla.ejs:
var id = <%= id %>
Now, I am trying to get that id para but it's showing undefined. I am using Express and EJS, and once I get that ID I want to manipulate an EJS file and send back a JS file which adapts itself according to the ID my node app received.
Thank you.
The way to do this would probably be like this - In your HTML you would request the JS file like so:
<script src="/blabla.js?id=545234677">
And then for express you would do the following:
app.get('/blabla.js', function(req, res, next) {
console.log(req.query.id);
res.setHeader('content-type', 'text/javascript');
// Pass id into the renderer
res.render('blabla', {id: req.query.id}); //default
});
Although rendering is normally used to render HTML. There is probably a better way to do what you want in a more elegant way.
Note: req.params would work, but would make the url look like /blabla.js/545234677. See the documentation on req.params.
How about changing your script to
<script src="[filename][id].js"></script>
EG:<script src="blabla123.js"></script>
then you can do something using regular expressions like
app.get(/^\/([a-zA-z]*)([0-9]*).js/,function(req,res,next){
var fileName = req.params['0'],
id = req.params['1'];
console.log(fileName);
console.log(id);
// Now since you have both fileName and id as you required, you can
// use it to dynamically serve the content
// if(file=='' && id==''){//logic}
// If you want static file Name, modify the regular expression accordingly
})
Just modify the regular expression in above example as per your need and its done. Currently the above regex matches for any of
/.js
/[numeric].js
/[alphabet].js
/[alphabet][numerals].js
I hope this helps.
Is there any way to redirect to an HTML file from a Node.JS application with something like: res.sendFile of express and pass a JSON data along to the html file?
I know this is late but I wanted to offer a solution which no one else has provided. This solution allows a file to be streamed to the response while still allowing you to modify the contents without needing a templating engine or buffering the entire file into memory.
Skip to the bottom if you don't care about "why"
Let me first describe why res.sendFile is so desirable for those who don't know. Since Node is single threaded, it works by performing lots and lots of very small tasks in succession - this includes reading from the file system and replying to an http request. At no point in time does Node just stop what it's doing and read an entire from the file system. It will read a little, do something else, read a little more, do something else. The same goes for replying to an http request and most other operations in Node (unless you explicitly use the sync version of an operation - such as readFileSync - don't do that if you can help it, seriously, don't - it's selfish).
Consider a scenario where 10 users make a request for for the same file. The inefficient thing to do would be to load the entire file into memory and then send the file using res.send(). Even though it's the same file, the file would be loaded into memory 10 separate times before being sent to the browser. The garbage collector would then need to clean up this mess after each request. The code would be innocently written like this:
app.use('/index.html', (req, res) => {
fs.readFile('../public/index.html', (err, data) => {
res.send(data.toString());
});
});
That seems right, and it works, but it's terribly inefficient. Since we know that Node does things in small chunks, the best thing to do would be to send the small chunks of data to the browser as they are being read from the file system. The chunks are never stored in memory and your server can now handle orders of magnitude more traffic. This concept is called streaming, and it's what res.sendFile does - it streams the file directly to the user from the file system and keeps the memory free for more important things. Here's how it looks if you were to do it manually:
app.use('/index.html', (req, res) => {
fs.createReadStream('../public/index.html')
.pipe(res);
});
Solution
If you would like to continue streaming a file to the user while making slight modifications to it, then this solution is for you. Please note, this is not a replacement for a templating engine but should rather be used to make small changes to a file as it is being streamed. The code below will append a small script tag with data to the body of an HTML page. It also shows how to prepend or append content to an http response stream:
NOTE: as mentioned in the comments, the original solution could have an edge case where this would fail. For fix this, I have added the new-line package to ensure data chunks are emitted at new lines.
const Transform = require('stream').Transform;
const parser = new Transform();
const newLineStream = require('new-line');
parser._transform = function(data, encoding, done) {
let str = data.toString();
str = str.replace('<html>', '<!-- Begin stream -->\n<html>');
str = str.replace('</body>', '<script>var data = {"foo": "bar"};</script>\n</body>\n<!-- End stream -->');
this.push(str);
done();
};
// app creation code removed for brevity
app.use('/index.html', (req, res) => {
fs
.createReadStream('../public/index.html')
.pipe(newLineStream())
.pipe(parser)
.pipe(res);
});
You get one response from a given request. You can either combine multiple things into one response or require the client to make separate requests to get separate things.
If what you're trying to do is to take an HTML file and modify it by inserting some JSON into it, then you can't use just res.sendFile() because that just reads a file from disk or cache and directly streams it as the response, offering no opportunity to modify it.
The more common way of doing this is to use a template system that lets you insert things into an HTML file (usually replacing special tags with your own data). There are literally hundreds of template systems and many that support node.js. Common choices for node.js are Jade (Pug), Handlebars, Ember, Dust, EJS, Mustache.
Or, if you really wanted to do so, you could read the HTML file into memory, use some sort of .replace() operation on it to insert your own data and then res.send() the resulting changed file.
Well, it's kinda old, but I didn't see any sufficient answer, except for "why not". You DO have way to pass parameters IN static file. And that's quite easy. Consider following code on your origin (using express):
let data = fs.readFileSync('yourPage.html', 'utf8');
if(data)
res.send(data.replace('param1Place','uniqueData'));
//else - 404
Now for example, just set a cookie, in yourPage.html, something like:
<script>
var date = new Date();
document.cookie = "yourCookieName='param1Place';" +
date.setTime(date.getTime() + 3600) + ";path=/";
</script>
And you can plainly pull content of uniqueData from yourCookieName wherever you want in your js
I think the answer posted by Ryan Wheale is the best solution if you actually want to modify something within an HTML file. You could also use cheerio for working with complex logic.
But in regards to this particular question where we just want to pass some data to the client from the server, there's actually no need to read index.html into memory at all.
You can simply add the following script tag somewhere at the top of your HTML file:
<script src="data.js"></script>
And then let Express serve that file with whatever data needed:
app.get("/data.js", function (req, res) {
res.send('window.SERVER_DATA={"some":"thing"}');
});
This data can then easily be referenced anywhere in your client application using the window object as: window.SERVER_DATA.some
Additional context for a React frontend:
This approach is especially useful during development if your client and server are running on different ports such as in the case of create-react-app because the proxied server can always respond to the request for data.js but when you're inserting something into index.html using Express then you always need to have your production build of index.html ready before inserting any data into it.
Why not just read the file, apply transformations and then set up the route in the callback?
fs.readFile(appPath, (err, html) => {
let htmlPlusData = html.toString().replace("DATA", JSON.stringify(data));
app.get('/', (req, res) => {
res.send(htmlPlusData);
});
});
Note that you can't dynamically change data, you'd have to restart the node instance.
You only have one response you can return from the server. The most common thing to do would be to template your file on the server with nunjucks or jade. Another choice is to render the file on the client and then to use javascript to make an ajax call to the server to get additional data. I suppose you could also set some data in a cookie and then read that on the client side via javascript as well.
(Unless you want to template the html file to insert the json data into a script tag). You'll need to expose an api endpoint in express the send along the data to the page, and have a function on the page to access it. for example,
// send the html
app.get('/', (req, res) => res.sendFile('index'));
// send json data
app.get('/data', (req, res) => res.json(data));
Now on the client side you can create a request to access this endpoint
function get() {
return new Promise((resolve, reject) => {
var req = new XMLHttpRequest();
req.open('GET', '/data');
req.onload = () => resolve(req.response);
});
}
// then to get the data, call the function
get().then((data) => {
var parsed = JSON.parse(data);
// do something with the data
});
EDIT:
So arrow functions probably don't work client side yet. make sure to replace them with function(){} in your real code
This is pretty easy to do using cookies. Simply do this:
On the server side -
response.append('Set-Cookie', 'LandingPage=' + landingPageCode);
response.sendFile(__dirname + '/mobileapps.html');
On client side -
<!DOCTYPE html>
<html>
<body onload="showDeferredLandingPageCode()">
<h2>Universal Link Mobile Apps Page</h2>
<p>This html page is used to demostrate deferred deeplinking with iOS</p>
</body>
<script language="javascript">
function showDeferredLandingPageCode() {
alert(document.cookie);
}
</script>
</html>
I am using node.js. I want to read a file with some placeholder strings and replace them dynamically before I serve the file. This is not an HTML file, so a templating engine will not work.
How can I do this?
If a template engine is overkill just use string.replace().
temp = "Hello %NAME%, would you like some %DRINK%?";
temp = temp.replace("%NAME%","Michael Dillon");
temp = temp.replace("%DRINK%","tea");
console.log(temp);
With only a bit more work you could make a general purpose template function based on just the standard methods in the String object.
Templating engines are not only for html. If you are using Express, for instance, you can set your own headers and specify a content-type:
View:
var foo = "{{ bar }}";
Rendering:
app.get('/file.js', function(req, res, next) {
res.render('templateName', {
locals: {bar: 'quux'},
headers: {'content-type': 'text/javascript'}
});
})
Will yield:
var foo = "quux";
If you are not using Express, you can just render the template and send the response with any content-type you like.