At the beginning of this year, I planned to rewrite my blog program based on the Express framework using Node.js, and say goodbye to ASP.NET from now on. However, the VPS I currently use is Windows Server system and IIS server. If Express and IIS both listen to port 80, there will be obvious conflicts. Fortunately, there is an extension called iisnode that hosts Node.js programs to IIS. Moreover, after hosting, it also means that various functions in IIS can be used (process management, GZip compression, logging, cache, permission control, domain name binding, etc.).
To use iisnode, you need to install:
1.Node.js
2. IIS URL Rewrite module
3.iisnode
After installation, you still follow the usual operations to create a site in the IIS manager and point to the directory of the Express program. The key is to add a web.config file:
The code copy is as follows:
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="bin/www" verb="*" modules="iisnode" resourceType="Unspecified" requireAccess="Script" />
</handlers>
<rewrite>
<rules>
<rule name="all">
<match url="/*" />
<action type="Rewrite" url="bin/www" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
This content can also be configured through the visual interface of the IIS Manager. It roughly means rewriting all requests to bin/www, and running bin/www using iisnode extension. However, after opening the site, an error message appears:
The code copy is as follows:
The request filter module is configured to reject paths in the URL containing the hiddenSegment section
At first I felt that I was not clear about it, but later I suddenly realized that the bin directory in ASP.NET is a special directory that is not allowed to be accessed. Rewrite the request to bin/www, which hits this rule. So, just change the directory name, for example, change bin to launch (it turns out to be not a good practice, let’s talk about it later), and web.config also needs to be adjusted accordingly:
The code copy is as follows:
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="launch/www" verb="*" modules="iisnode" resourceType="Unspecified" requireAccess="Script" />
</handlers>
<rewrite>
<rules>
<rule name="all">
<match url="/*" />
<action type="Rewrite" url="launch/www" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
After restarting the site in IIS Manager, accessing it again, it finally started running, it was not easy! But I was still too happy.
During the process of testing the program function, it was found that the obtained IP was empty. In the Express framework, IP is obtained through req.ip, which in turn obtains the value from REMOTE_ADDR of the request header. Through a simple test code, it was found that the value of REMOTE_ADDR is also empty. It is obvious that this header information is lost during the process from IIS to Node.js. After Google, I found that iisnode does have this problem. The official solution is to use X-Forword-For, but I found another solution.
There is a configuration in Web.config (before adding it to </system.webServer>) that can retain REMOTE_ADDR:
The code copy is as follows:
<iisnode promoteServerVars="REMOTE_ADDR" />
According to the instructions, the reserved REMOTE_ADDR will be renamed to x-iisnode-REMOTE_ADDR, so you have to overwrite the value of req.ip once and add a middleware function to the app.js of Express:
The code copy is as follows:
app.use(function(req, res, next) {
req.ip = req.headers['x-iisnode-REMOTE_ADDR'];
next();
});
However, after this adjustment, the IP obtained is still empty, which inevitably makes people wonder whether the assignment of req.ip has failed. Looking at the source code of Express, you will find that req.ip is defined through the define getter, so to overwrite it, you have to define again:
The code copy is as follows:
app.use(function(req, res, next) {
Object.defineProperty(req, 'ip', {
get: function() { return this.headers['x-iisnode-REMOTE_ADDR']; }
});
next();
});
This problem has finally been solved, but this is not a good method. It will be troublesome if Express sets req.ip to read-only in the future.
Continue testing and find another problem. Normally, the file upload function in the blog background will pass the file to the public/upload directory, but in fact, the public/upload folder is generated in the launch directory (that is, the original bin directory). In fact, the reason is that the www file as the program entrance is in the launch directory, so the launch directory becomes the execution directory of the application. My solution is to change the name of the launch directory back to bin, create a launch.js in the root directory to call bin/www:
The code copy is as follows:
#!/usr/bin/env node
require('./bin/www');
Then change the program entry to launch.js:
The code copy is as follows:
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="launch.js" verb="*" modules="iisnode" resourceType="Unspecified" requireAccess="Script" />
</handlers>
<rewrite>
<rules>
<rule name="all">
<match url="/*" />
<action type="Rewrite" url="launch.js" />
</rule>
</rules>
</rewrite>
<iisnode promoteServerVars="REMOTE_ADDR" />
</system.webServer>
</configuration>
Obviously, iisnode is not a mature product, and of course Node.js is not (not 1.0 yet), everything needs further exploration and improvement.