nodejs + express + swig: how do I check the request before rendering? - node.js

To be honest, I don't really know how nodejs, express, and swig work.
I have this code that seems to serve up all my html pages
app.engine('html', swig.renderFile);
app.set('view engine', 'html');
app.set('views', __dirname+'/html');
I'd like to be able to examine the request before returning the html file. Specifically, I want to check if the user is using an older version of IE and redirect them to another page instead.
Now, I can examine the request and redirect for specific pages like so
app.get('/veryspecificpage.html', function(req, res) {
if( isBadBrowser(req) ) {
res.writeHead(302, {
'Location': 'browserError.html'
});
res.end();
} else {
res.render('veryspecificpage', {});
});
})
But I don't want to have to specify this for every single .html page. How do I intercept the request and do this for all html pages?

Log each request
Sample node web server. Simply log each request to the server like this...
var http = require('http');
http.createServer(function (req, res) {
console.log(req); // <--- LOGS EACH REQUEST
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello World\n');
}).listen(1337, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');

You should use middleware to check every request.
// gets executed for every request to the app
app.use(function (req, res, next) {
// check for browser here and redirect if necessary, if not, next() will continue to other middleware/route.
next();
});
Make sure you place it before any route. You can learn more about it by going to this page.

Related

Intercept request for a static file in express.js

I have a node server, that serves static files in a PUBLIC folder like this:
var app = express();
app.listen(port);
app.use(compression());
app.use(express.static(__dirname + '/PUBLIC'));
There is a json file, let's say important.json that is located in /PUBLIC folder. This is being served as a static file
Now, I want to intercept request for this /PUBLIC/important.json, so that I can programatically return a random json structure instead.
None of the followings works:
app.get('/PUBLIC/important.json', function(req, res) {
console.log("caught1!")
});
app.get(__dirname + '/PUBLIC/important.json', function(req, res) {
console.log("caught2!")
});
app.get('important.json', function(req, res) {
console.log("caught3!")
});
How can I intercept request for that partically static file?
As the express.static middleware does not call the next middleware using next(), the definition order is important. You have to define your own middleware before using express.static.
app.get('/PUBLIC/important.json', (req, res, next) => {
console.log('caught');
next();
});
app.use(express.static(__dirname + '/PUBLIC'));
Could you tell us a bit more about your stack ?
Are you using nginx / apache to proxy_pass the traffic to your nodejs server ?
Are you just running your app with "node app.js"
Let's try to add this simple route in your application :
app.get('/', function (req, res) {
res.send('Hello World!');
});
And try to access it by removing URI parameters ? Does the "Hello world" show up ?
I just want to be sure the traffic is actually treated by your node app.
Your route definition is supposed to work for your actual request.

Need to understand why expressjs is redirecting to index.html

I have the following server file, using express:
var express = require('express');
var app = express();
var port = process.env.PORT || 8080;
app.listen(port);
console.log('Listening on port: ' + port);
// get an instance of router
var router = express.Router();
app.use('/', router);
app.use(express.static(__dirname + "/"));
// route middle-ware that will happen on every request
router.use(function(req, res, next) {
// log each request to the console
console.log(req.method, req.url + " logging all requests");
// continue doing what we were doing and go to the route
next();
});
// home page route for port 8080, gets executed when entering localhost:8080
// and redirects to index.html (correctly and as expected)
router.get('/', function(req, res) {
console.log("routing from route")
res.redirect('index.html');
});
// This gets executed when my url is: http://localhost:8080/test
// and redirects to index.html (the questions is why!? I thought
// all the requests to root route would be caught by the router instance
app.get('*', function(req, res){
console.log('redirecting to index.html');
res.redirect('/index.html');
});
Looking at the code above and my comments, I cannot understand why
app.get('*', function(){...})
does not get executed when URL is
localhost:8080/index.html but gets executed when URL is localhost:8080/test
Even though, this is the behavior that I was hoping for, I'm not sure why this works?
I don't have a "test.html" page in the root.
One other thing, the index.html does load other scripts, so I expected
app.get('*', function(){...})
to get executed for such get requests too, as it is supposed to be the catch all, but it does not.
Does app.use('/', router) mean that any route that has single character "/" should be handled by Router instance (as long as not a static file)? so "http:localhost:8080" gets interpreted as "http://localhost:8080/"?
I would appreciate any explanation.
This line-
app.use(express.static(__dirname + "/"));
will run first. It will see that index.html exists and serve that file statically.

Node.js catch-all route with redirect always renders index page with Angular regardless of url

Essentially when I use a catch-all route and use res.redirect('/') regardless of the url I enter it will always render the index/home page (ie Angular does not seem to 'see' the full url) however if I place res.render('index') in the catch-all route everything works fine. I don't want repeat code and redirecting to '/' should work, I have probably made a stupid mistake somewhere here and any help would be much appreciated!
Angular routing:
app.config(function ($routeProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider
.when('/',
{
templateUrl: 'partials/home.jade'
})
.when('/about',
{
templateUrl: 'partials/about.jade'
})
.otherwise( {redirectTo: '/'});
});
This will correctly render the about page when entering site-address/about:
app.get('/', function (req, res) {
res.render('index');
});
app.get('/partials/:name', function (req, res) {
res.render('partials/' + req.params.name);
});
app.get('*', function (req, res) {
res.render('index');
});
This will always just show the index page:
app.get('/', function (req, res) {
res.render('index');
});
app.get('/partials/:name', function (req, res) {
res.render('partials/' + req.params.name);
});
app.get('*', function (req, res) {
res.redirect('/');
});
Configuration if it helps:
// Configuration
app.configure(function () {
app.set('port', process.env.PORT || 1337);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.static(path.join(__dirname, 'public')));
app.use(app.router);
});
This is by design.
When you use res.redirect('/'), Node/Express is sending an HTTP redirect, which will change the URL in the browser, thus when your index template is rendered, and when the angular code is run, the URL is /, regardless of what the user entered (the whole point of the redirect).
When you omit the redirect and just send the template as a response, NodeJs responds with an HTTP 200 (success) along with HTML content. Since the URL didn't change, when your application runs, the angular routing properly routes.
EDIT TO ADD: Address Comment
Rather than have two routes render the same template, I would get rid of the / route all together, and just have the catch-all render the index template, which will then turn control over to the Angular router.
Otherwise, I would consider splitting your routes conceptually: All your application routes are specifically sent to angular router, and you render static routes via nodejs, and use your catch all to render a more appropriate page for a missing or unknown resource (more helpful for your users).
You can use regex-like languages to specify a single handler:
app.get('/()|(about)|(contact)/',function(req,res) {/* handle */});
For folder structure:
root
-web.js
-dist/index.html
-dist/rest of the page
Just paste the following snippet to your web.js
nodeApp.use('/', express.static(__dirname + '/dist'));
nodeApp.get('/[^\.]+$', function(req, res){
res.set('Content-Type', 'text/html')
.sendfile(__dirname + '/dist/index.html');
});
I have a problem where when I use the catch-all routing, all of my static assets (stylesheets, javascript files etc) don't load:
app.use(app.router);
app.use(express.static(path.join(__dirname, frontend.app)));
// when this is removed, I can load static assets just fine
app.get('*', function(req, res){
res.render('main');
});
When the app.get('*', ...) part is removed, I can load the static assets just fine (i.e. I can type in 'examplejavascript.js' and see the javascript file. When it is there however, express catches the assets.
$locationProvider.html5Mode(true);
are you sure your requests are hitting the nodejs server. using this means, angularjs will try to search for urlmapping inside browser.
try by commenting
$locationProvider.html5Mode(true);

How to show the node.js output in HTMLPage

I am using webmatrix and building node.js application.
In that i want to pass the value from the node.js to the HTMLPage in the project.
var http = require('http');
var URL = require('url');
http.createServer(function (req, res)
{
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('Hi man');
}).listen(process.env.PORT || 8080);
With that code i tried it gives a page with hi man.Its obvious.But i want this to be rendered in the html page i am having in the project.
How to achieve tat.
Please give some suggestion on this..
First I recommend you use Expressjs, which is a framework for Nodejs.
Then I recommend you use EJS, an engine template for Nodejs.
When integreses all, you can use code similar to:
...
app.set('views', __dirname + '/views');
app.register('.html', require('ejs'));
app.set('view engine', 'html');
app.use(express.static(__dirname + '/public'));
app.use(express.favicon(__dirname + '/public/img/favicon.ico', { maxAge: 2592000000 }));
app.use(expressValidator);
...
And
...
app.get('/test', function(req, res){
res.render('test', {msg: "Hello World"});
});
...
Finally in your file views/test.html:
...
<div><%- msg %></div>
...
I hope it helps somewhat.
Additionally review:
http://utahjs.com/2010/09/25/nodejs-express-and-ejs-templates/
Render ejs file in node.js
Node.js - EJS example
Greetings.
Sounds like you want to request your nodejs server using AJAX and inject the server's response into an HTML element.
Check out: http://api.jquery.com/jQuery.getJSON/
Here's an example using jQuery to connect and retrieve the server response from node running on port 8080 of different host, and insert that into an HTML element.
node:
var http = require('http');
var url = require('url');
http.createServer(function (req, res)
{
//parse the url and query string from the request
var url_parts = url.parse(req.url, true);
//if the callback query parameter is set, we return the string (or object)
if(url_parts.query.callback){
var str = "Hi man";
res.writeHead(200, {'Content-Type':'text/html'});
res.end(url_parsed.query.callback+'("'+str+'")');
//if it's not set, let's return a 404 error
}else{
res.writeHead(404, { 'Content-Type': 'text/html' });
res.end('404 Error');
}
}).listen(process.env.PORT || 8080);
index.html
<div id="my-div"></div>
<script src="http://code.jquery.com/jquery-latest.js"></script>
<script>
$(function(){
//getJSON and the callback paramater make us cross-domain capable.
$.getJSON('http://myotherhost.com:8080/?callback=?', function(data){
$("#my-div").html(data);
});
});
</script>

How does Node.js render views?

In a basic Node.js application with a single app.js file and one index.html document where in app.js the following is specified, then firing up a server and visiting localhost:8080 works just fine:
server = http.createServer( function(req, res) {
fs.readFile('index.html', function(err, page) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(page);
res.end();
});
fs.readFile('new.html', function(err, page) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(page);
res.end();
});
});
server.listen(8080);
However, when duplicating index.html to another file like new.html and editing certain content, then adding an link into index.html linking to the new page, clicking on the link will render the same content as in index.html. In fact, linking to any non-existent html page will append the subsequent page to the URL but keep showing index.html's contents.
Following a suggestion of rewriting the fs.readFile line to be:
fs.readFile(req.url, function(err, page) { ...
Then going to localhost:8080 loads new.html's contents for some reason I don't understand. How should one render out these views?
Following on the other answers given and also recommending express. But first, the short answer to the question "how does node.js render views?" is: it doesn't.
When you build a node application you are building a small web server, using the minimal building blocks node gives you like http.createServer(). It's up to you to write the logic to choose what to send in response to a request.
Or you can use an existing framework like Express. Here is the solution using express:
Install express:
npm install express
Then, in your app.js:
var express = require('express');
var app = express.createServer();
app.get('/index.html', function(req,res) {
res.render('index.html');
});
app.get('/new.html', function(req,res) {
res.render('new.html');
});
app.listen(8080)
You can also use EJS or Jade as the template language by adding this before the createServer line:
app.set('view engine', 'ejs');
or:
app.set('view engine', 'jade');
Because you need to have conditions on your request access url (req.url).
Right now it closes the response on your first res.end() regardless of your url, and never reach the rest of your code (well it does but the response already fired before so it has no effect).
try this:
server = http.createServer( function(req, res) {
if (req.url == '/index.html') { //will be executed only on index.html
fs.readFile('index.html', function(err, page) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(page);
res.end();
});
}
if (req.url == '/new.html') { //will be executed only for new.html
fs.readFile('new.html', function(err, page) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(page);
res.end();
});
}
});
server.listen(8080);

Resources