Friday, November 1, 2013

How to run an IISNODE (node.js for IIS) app in a virtual directory OR how to build a node.js app that works regardless of virtual directories in IIS

How's THAT for a lengthy title?

I spent more time than I care to admit trying to figure out how to get an existing node.js app running in a virtual directory on IIS. The entire reason I set out to use node on IIS was to be able to use it inside an existing app and that meant either a sub-app or a virtual directory and in this case, the latter seemed the better (easier) choice but the virtual directory name was causing reference issues for "Dynamic" requests (actual node.js app files).

The solution turns out to be well addressed, at least for "express" framework apps using app.use()

When you call app.use(app.router), you can pass a namespace prefix for your virtual directory and then requests will succeed BUT who would want to hard-code their virtual directory name into their app [yuck!]?

The solution turns out to be quite simple because IISNODE promotes appSettings to environment variables. Taking advantage of this fact, you can add an entry to the apps web.config such as:

        <add key="appvirtdir" value="/nodetest" />

Then you can make one simple modification to your server.js file like this:

app.configure(function () {
    app.use(process.env.appvirtdir || '', app.router);

This way your app is flexible but not even tied to the existence of the environment variable let alone its value UNLESS you want it to be and even then, the variable is local to the app (not really an environment variable).

I'm still working on the static requests and will update this post when I nail that. From what I'm reading everywhere, it should be as simple as updating the relevant rewrite rule but so far it seems that the rule isn't being applied so the settings are irrelevant. I'm still trying to determine why but I'm not quite competent with URLRewrite. Below is what I currently have.

My virtual directory is "nodetest"
~/public/stylesheets/style.css exists
/nodetest/stylesheets/style.css returns "Cannot GET /nodetest/stylesheets/style.css"
/nodetest/public/stylesheets/style.css returns the expected CSS

              <rule name="LogFile" patternSyntax="ECMAScript" stopProcessing="true">
                <match url="^(?:[a-zA-Z0-9_\-]*/)*[a-zA-Z0-9_\-]+\.js\.logs\/\d+\.txt$" />
              <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
                  <match url="^server.js\/debug[\/]?" />
              <rule name="StaticContent" patternSyntax="Wildcard">
                <action type="Rewrite" url="public/{R:0}" logRewrittenUrl="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                <match url="*.*" />
              <rule name="DynamicContent">
                      <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True" />
                  <action type="Rewrite" url="server.js" />


Rudy Schockaert said...

Hello Michael,
I didn't see a follow-up blog explaining how you solved your issue with static content.
I based my solution on the method you used for your appvirtdir.

In my server.js I added the following line:

app.locals.appPubDir = process.env.appPubDir || '';

In my views\layout.jade I use :

link(rel='stylesheet', href='#{appPubDir}/stylesheets/style.css')

So, whenever there is an Environment variable appPubDir its value gets added in front of any static file path I use.

The servicing of these static files is still done by IIS because of the rewriteUrl rules.

Nathan Daniels said...

Thanks for this. Been working on this same issue for the last couple of days.

Murillo said...

Hello Rudy,

I put your suggestions into practice, but just noting that if we put a point before the slash (./stylesheets/style.css), at least in my case, it will work properly.


Post a Comment