Search code examples
node.jsiisweb-applicationsweb-configiisnode

IIS Node Application won't run correctly with Web.Config?


I have a Node Application I'd like to run using IIS. The problem is my configuration file. I am using IIS 10.0 and don't know how to configure everything correctly? Whenever I include both of these rules in my Web.Config file...

<!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
<rule name="StaticContent" patternSyntax="Wildcard">
    <action type="Rewrite" url="public/{R:0}" logRewrittenUrl="true"/>
        <conditions>
            <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
        </conditions>
        <match url="*.*"/>
</rule>

<!-- All other URLs are mapped to the Node.js application entry point -->
<rule name="DynamicContent">
     <conditions>
         <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
     </conditions>
     <action type="Rewrite" url="server.js"/>
 </rule>

...I get a 500.1000 Error that looks like this:

500.1000 ERROR

When I take one or the other out, my websites pages and links load correctly but ALL POST requests create a 404 error? Because it's searching for a physical path instead of calling a POST function in my server.js file?

Here is the 404 ERROR:

404 ERROR

I know that my site works, because it works (GET & POST requests) in the debugger for VS 2015. Here are snippets from my code and the configuration file.

Here is the full Web.Config file :

<?xml version="1.0" encoding="utf-8"?>

<configuration>
    <appSettings>
        <!--
            All appSettings are made available to your Node.js app via environment variables
            You can access them in your app through the process.env object.

            process.env.<key>
        -->

       <!-- Unconmment the below appSetting if you'd like to use a Virtual Directory -->
       <!-- <add key="virtualDirPath" value="" /> -->
    </appSettings>
    <system.webServer>
        <iisnode node_env="%node_env%"
                 nodeProcessCountPerApplication="1"
                 maxConcurrentRequestsPerProcess="1024"
                 maxNamedPipeConnectionRetry="100"
                 namedPipeConnectionRetryDelay="250"
                 maxNamedPipeConnectionPoolSize="512"
                 maxNamedPipePooledConnectionAge="30000"
                 asyncCompletionThreadCount="0"
                 initialRequestBufferSize="4096"
                 maxRequestBufferSize="65536"
                 uncFileChangesPollingInterval="5000"
                 gracefulShutdownTimeout="60000"
                 loggingEnabled="true"
                 logDirectory="iisnode"
                 debuggingEnabled="true"
                 debugHeaderEnabled="false"
                 debuggerPortRange="5058-6058"
                 debuggerPathSegment="debug"
                 maxLogFileSizeInKB="128"
                 maxTotalLogFileSizeInKB="1024"
                 maxLogFiles="20"
                 devErrorsEnabled="true"
                 flushResponse="false"
                 enableXFF="false"
                 promoteServerVars=""
                 configOverrides="iisnode.yml"
                 watchedFiles="web.config;*.js"
                 nodeProcessCommandLine="C:\Program Files\nodejs\node.exe"/>

        <!-- 
            Before the handlers element can work on IIS 8.5
            follow steps listed here https://github.com/tjanczuk/iisnode/issues/52 
        -->                 
        <handlers>
            <add name="iisnode" path="server.js" verb="*" modules="iisnode"/>

            <!-- Uncomment below handler if using Socket.io -->
            <!--<add name="iisnode-socketio" path="server.js" verb="*" modules="iisnode" />-->
        </handlers>

        <rewrite>
            <rules>
                <!-- Don't interfere with requests for node-inspector         debugging -->
                <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
                    <match url="^server.js\/debug[\/]?"/>
                </rule>

                <!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
                <rule name="StaticContent" patternSyntax="Wildcard">
                    <action type="Rewrite" url="public/{R:0}" logRewrittenUrl="true"/>
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
                    </conditions>
                    <match url="*.*"/>
                </rule>

                <!-- All other URLs are mapped to the Node.js application entry point -->
                <rule name="DynamicContent">
                    <conditions>
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
                    </conditions>
                    <action type="Rewrite" url="server.js"/>
                </rule>

                <rule name="SocketIO" patternSyntax="ECMAScript">
                    <match url="socket.io.+"/>
                    <action type="Rewrite" url="server.js"/>
                </rule>
            </rules>
        </rewrite>
        <directoryBrowse enabled="false"/>
    </system.webServer>
</configuration>

Here is the general structure of my server.js file :

var http = require('http');
var express = require('express');
var fs = require("fs");
var sql = require("mssql");
var bodyParser = require('body-parser');         // Used to parse incoming messages
var Connection = require('tedious').Connection;  // Added for Azure

var app = express();
var port = process.env.port || 1337;

// pulls the index.html file
app.use(express.static(__dirname + "/"));

// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))

// parse application/json
app.use(bodyParser.json())

// DATABASE configuration
var config = {
    userName: 'USERNAME',
    password: 'PASSWORD',
    server:   'SERVER',   
    options: {
        encrypt: true,
        database: 'DATABASE',
        rowCollectionOnRequestCompletion: true
    }
};
var connection = new Connection(config);


// Server that listens to port 80
var server = http.createServer();

var server = app.listen(port, function () {

    var host = server.address().address
    var port = server.address().port

    console.log("Node Js app listening at http://%s:%s", host, port)

});



app.post('/postrequest', function (req, res) {
    // function 
});

Thanks for the help. I can't seem to figure out this issue.

I make post requests in a separate script that is in a separate folder in this directory called scripts:

xhttp.open("POST", "http://localhost:80/My Application/postrequest", false);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send("date=" + date + "&name=" + name + "&type=" + type);

Solution

  • I was able to successfully publish my Node Web Application to IIS by using the following configuration in my Web.Config :

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <system.webServer>
        <handlers>
          <add name="iisnode" path="server.js" verb="*" modules="iisnode" />
        </handlers>
    
        <rewrite>
          <rules>
             <!-- Don't interfere with requests for node-inspector debugging -->
             <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
                <match url="^server.js\/debug[\/]?"/>
             </rule>
    
             <!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
             <rule name="StaticContent" patternSyntax="Wildcard">
                   <action type="Rewrite" url="public/{R:0}" logRewrittenUrl="true"/>
                   <conditions>
                      <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
                   </conditions>
                <match url="*.*"/>
             </rule>
    
             <!-- All other URLs are mapped to the Node.js application entry point -->
             <rule name="DynamicContent">
                   <conditions>
                      <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
                   </conditions>
                <action type="Rewrite" url="server.js"/>
             </rule>
    
             <rule name="SocketIO" patternSyntax="ECMAScript">
                   <match url="socket.io.+"/>
                <action type="Rewrite" url="server.js"/>
             </rule>
          </rules>
        </rewrite>
    
        <iisnode promoteServerVars="LOGON_USER" />
    
      </system.webServer>
    </configuration>
    

    AND by changing my server.js file to :

    var http = require('http');
    var express = require('express');
    var bodyParser = require('body-parser');         // Used to parse incoming messages
    var port = process.env.port;
    
    var app = express(function(req, res){
        username = req.headers['x-iisnode-auth_user'];
    });
    
    app.use(express.static(__dirname + "/ApplicationNameOnIIS/"));
    
    app.use(bodyParser.urlencoded({ extended: false }))
    
    app.use(bodyParser.json())
    
    // Start listening on server port
    app.listen(process.env.PORT);
    
    app.get('/ApplicationNameOnIIS/', function(req, res) {
        res.sendfile(__dirname + '/index.html');    
    });
    
    // ...The Rest Of My Code...