Node.js + Express based app not running with iisnode

eca picture eca · Jun 9, 2014 · Viewed 13.4k times · Source

I am doing some experiments with node.js + express and iisnode. I have the following very simple app, located in C:\tsapp-deploy\tsappsvr\TestExpress\0.0.0:

app.js:

var express = require('express');
var app = express();
app.use(express.static(__dirname + "/public"));

var port = process.env.PORT || 2709;
app.listen(port, function() {
  console.log('Listening on port ' + port);
});

package.json:

{
  "name": "TestExpress",
  "version": "0.0.0",
  "private": true,
  "dependencies": {
    "express": "3.x"
  }
}

web.config:

<configuration>
  <system.webServer>
    <handlers>
      <add name="iisnode" path="app.js" verb="*" modules="iisnode" />
    </handlers>
  </system.webServer>
</configuration>

public/index.html:

<!doctype html>
<html>
<head>
  <script language="javascript" type="text/javascript" src="js/jquery.js"></script>
  <script language="javascript" type="text/javascript">
    $(document).ready(function() {
      console.log("READY!");
      $("#hello").html("Hello, World!");
    });
  </script>
</head>
<body>
  <h1 id="hello" style="text-align:center;"></h1>
</body>
</html>

My configuration is: Windows 8.1, IIS 8; iisnode version 0.2.11, node version v0.10.28.

When launched from command line (C:\tsapp-deploy\tsappsvr\TestExpress\0.0.0>node app.js), the app runs as expected: in the browser, I go to http://localhost:2709/ and see "Hello, World!".

My IIS is currently running other node.js-based applications not using express from similar locations (i.e. from C:\tsapp-deploy\tsappsvr\appname\x.y.z\index.js), so I assume it should be correctly configured, but when I try to run this app from the browser, typing http://localhost/tsappsvr/TestExpress/0.0.0/app.js I get a 404 Not Found (in IE) or a "Cannot GET /tsappsvr/TestExpress/0.0.0/app.js" in Chrome and Firefox.

I guess that the problem might be in my web.config, but I cannot figure out how to change it to get my app working. I have tried several changes to web.config as suggested in other answers to similar questions, but no success yet. Any suggestions?

Thanks in advance.

Answer

eca picture eca · Jun 16, 2014

I think I have found a solution to my problem:

  • First I installed the url-rewrite extension, thanks again, David.
  • Secondly I changed my web.config as follows:

web.config:

<configuration>
  <system.webServer>
    <handlers>
      <add name="iisnode" path="app.js" verb="*" modules="iisnode" />
    </handlers>
    <rewrite>
      <rules>
        <rule name="myapp">
          <match url="/*" />
          <action type="Rewrite" url="app.js" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
  <appSettings>
    <add key="deployPath" value="/tsappsvr/TestExpress/0.0.0" />
  </appSettings>
</configuration>

Please note the deployPath key in appSettings section, whose value is set to the app's virtual path (set in IIS). In my case it is /tsappsvr/TestExpress/0.0.0.

  • Finally, my app.js is now as follows:

app.js:

// Preamble, so to say
var express = require('express');
var http = require('http');
var app = express();

// Variable deployPath is set in web.config and must match
// the path of app.js in virtual directory.
// If app.js is run from command line:
//   "C:/tssapp-deploy/tsappsvr/TestExpress/0.0.0> node app.js"
// deployPath is set to empty string.
var deployPath = process.env.deployPath || "";

// Static content server
app.use(deployPath + "/pages", express.static(__dirname + '/public'));

// REST API handler (placeholder)
app.all(deployPath + "/api", function(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end("<h2>REST API Handler found.</h2>");
});

// Default handler
app.get(deployPath + "/", function(req, res) {
  res.writeHead(200, {'Content-Type': 'text/html'});
  res.end("<h2>Default Handler found.</h2>");
});

// Not Found handler
app.use(function(req, res, next){
  res.writeHead(404, {'Content-Type': 'text/html'});
  res.write("<h2>The requested resource is not available.</h2>");
  res.write("Request received on port " + process.env.PORT + "<br>");
  res.write("Request URL: " + req.url + "<br>");
  res.write("Deploy Path: " + deployPath + "<br>");
  res.end('[iisnode version is ' + process.env.IISNODE_VERSION + ', node version is ' + process.version + ']');
});

// Server creation
var server = http.createServer(app);
var port = process.env.PORT || 2709;
server.listen(port);

Variable deployPath is used as a prefix for all handlers (except the "Not Found" handler). When app.js is launched from iisnode, deployPath it is part of process.env, while it is undefined if app.js is launched from command line (e.g. C:/tsapp-deploy/tsappsvr/TestExpress/0.0.0>node app.js). This ensures that app.js works in both cases.

Now, typing http://localhost/tsappsvr/TestExpress/0.0.0/ in the browser I fire the default handler; appending /pages to the former URL I have access to static content, while appending /api I fire the REST API handler. The same happens for http://localhost:2709/ as base URL.