运行Node.js的IIS扩展iisnode安装配置笔记


Posted in Javascript onMarch 02, 2015

今年年初打算用Node.js基于Express框架重写博客程序,从此告别ASP.NET。然而,我目前用的VPS是Windows Server系统、IIS服务器,如果让Express和IIS都监听80端口,明显会产生冲突。幸好,有一个叫做iisnode的扩展可以把Node.js程序托管到IIS。而且,这样托管之后也意味着可以使用IIS里面的各种功能(进程管理、GZip压缩、日志、缓存、权限控制、域名绑定等)。

要使用iisnode,得安装:

1.Node.js
2.IIS的URL Rewrite模块
3.iisnode

装好之后,还是按照常规操作,在IIS管理器中创建站点,指向Express程序的目录,关键是还要增加一个web.config文件:

<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>

这段内容也可以通过IIS管理器的可视化界面配置。大概意思把所有请求重写到bin/www,而且使用iisnode扩展运行bin/www。然而,打开站点后,却出现了这样的错误提示:

请求筛选模块被配置为拒绝包含 hiddenSegment 节的 URL 中的路径

起初是觉得不明所以,后来突然醒悟,ASP.NET里面的bin目录是个不允许访问的特殊目录。把请求重写到bin/www,恰好命中了这条规则。所以呢,改一下目录名就好了,比如把bin改成launch(事实证明这不是好做法,后面再说),web.config也要对应调整:
<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>

在IIS管理器中重启站点后再次访问,终于运行起来了,不容易啊!不过还是高兴得太早了。

在测试程序功能的过程中,竟然发现获取到的IP为空。在Express框架中,IP是通过req.ip获取的,而req.ip又是从请求头的REMOTE_ADDR获取值。通过一段简单的测试代码,发现REMOTE_ADDR的值也为空。很明显,从IIS到Node.js的过程中,这段头信息丢失了。Google一番之后,发现iisnode确有此问题,官方提供的解决方案是使用X-Forword-For,不过我又发现了另外一个办法。

Web.config中有一段配置(加到</system.webServer>前)可以保留REMOTE_ADDR:

<iisnode promoteServerVars="REMOTE_ADDR" />

根据说明,保留的REMOTE_ADDR会被改名为x-iisnode-REMOTE_ADDR,所以还得把req.ip的值覆盖一次,在Express的app.js中增加一个中间件函数:

app.use(function(req, res, next) {

    req.ip = req.headers['x-iisnode-REMOTE_ADDR'];

    next();

});

然而,这样调整后,获取到的IP还是空,这不免让人怀疑,req.ip的赋值是不是失败了。看一下Express的源代码可以发现,req.ip是通过define getter的方式定义的,所以要覆盖它就得再define一次:
app.use(function(req, res, next) {

    Object.defineProperty(req, 'ip', {

        get: function() { return this.headers['x-iisnode-REMOTE_ADDR']; }

    });

    next();

});

这样问题终于解决了,但这不是一个好方法,要是以后Express把req.ip设成只读就麻烦了。

继续测试,又发现另外一个问题。正常来说,博客后台的文件上传功能会把文件传到public/upload这个目录下,但实际上却在launch目录(即原来的bin目录)下生成了public/upload文件夹。其实原因是作为程序入口的www文件是在launch目录下,所以launch目录成了应用程序的执行目录。我的解决办法是,把launch目录的名字改回bin,在根目录下创建一个launch.js去调用bin/www:

#!/usr/bin/env node
require('./bin/www');

然后把程序入口改为launch.js:

<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>

显然,iisnode还不是一个成熟的产品,当然Node.js也不是(至今还没1.0),一切都有待进一步探索和完善。

Javascript 相关文章推荐
JavaScript中valueOf函数与toString方法深入理解
Dec 02 Javascript
js批量设置样式的三种方法不推荐使用with
Feb 25 Javascript
js 使FORM表单的所有元素不可编辑的示例代码
Oct 17 Javascript
Javascript事件实例详解
Nov 06 Javascript
javascript实现超炫的向上滑行菜单实例
Aug 03 Javascript
AngularJS入门教程之路由与多视图详解
Aug 19 Javascript
微信小程序实现滑动删除效果
May 19 Javascript
vue2组件之select2调用的示例代码
Oct 12 Javascript
详解开源的JavaScript插件化框架MinimaJS
Oct 26 Javascript
three.js 制作动态二维码的示例代码
Jul 31 Javascript
JQuery通过键盘控制键盘按下与松开触发事件
Aug 07 jQuery
基于vue项目设置resolves.alias: '@'路径并适配webstorm
Dec 02 Vue.js
Javascript动画的实现原理浅析
Mar 02 #Javascript
JavaScript页面模板库handlebars的简单用法
Mar 02 #Javascript
EasyUI中实现form表单提交的示例分享
Mar 01 #Javascript
EasyUI实现二级页面的内容勾选的方法
Mar 01 #Javascript
EasyUI实现第二层弹出框的方法
Mar 01 #Javascript
EasyUI,点击开启编辑框,并且编辑框获得焦点的方法
Mar 01 #Javascript
浅谈EasyUI中Treegrid节点的删除
Mar 01 #Javascript
You might like
php 删除目录下N分钟前创建的所有文件的实现代码
2013/08/10 PHP
又一个PHP实现的冒泡排序算法分享
2014/08/21 PHP
Joomla语言翻译类Jtext用法分析
2016/05/05 PHP
ThinkPHP框架整合微信支付之刷卡模式图文详解
2019/04/10 PHP
javascript div 弹出可拖动窗口
2009/02/26 Javascript
jquery getScript动态加载JS方法改进详解
2012/11/15 Javascript
Javascript 字符串模板的简单实现
2016/02/13 Javascript
两种JavaScript的AES加密方式(可与Java相互加解密)
2016/08/02 Javascript
js设置文字颜色的方法示例
2016/12/30 Javascript
微信小程序 解决请求服务器手机预览请求不到数据的方法
2017/01/04 Javascript
JS+HTML5实现上传图片预览效果完整实例【测试可用】
2017/04/20 Javascript
nodejs6下使用koa2框架实例
2017/05/18 NodeJs
浅谈struts1 &amp; jquery form 文件异步上传
2017/05/25 jQuery
微信小程序input框中加入小图标的实现方法
2018/06/19 Javascript
JavaScript遍历数组的三种方法map、forEach与filter实例详解
2019/02/27 Javascript
linux服务器快速卸载安装node环境(简单上手)
2021/02/22 Javascript
python+opencv识别图片中的圆形
2020/03/25 Python
python 每天如何定时启动爬虫任务(实现方法分享)
2018/05/21 Python
Python利用字典破解WIFI密码的方法
2019/02/27 Python
Pandas DataFrame数据的更改、插入新增的列和行的方法
2019/06/25 Python
使用Python串口实时显示数据并绘图的例子
2019/12/26 Python
win10安装tesserocr配置 Python使用tesserocr识别字母数字验证码
2020/01/16 Python
Python class的继承方法代码实例
2020/02/14 Python
属性与 @property 方法让你的python更高效
2020/09/21 Python
今天学到的CSS最新技术(与图片背景相关)
2012/12/24 HTML / CSS
HearthSong官网:儿童户外玩具、儿童益智玩具
2017/10/16 全球购物
请编程遍历页面上所有 TextBox 控件并给它赋值为 string.Empty
2015/12/03 面试题
建筑毕业生自我鉴定
2013/10/18 职场文书
奥巴马英文演讲稿
2014/05/15 职场文书
文明城市标语
2014/06/16 职场文书
放飞梦想演讲稿800字
2014/08/26 职场文书
学生考试舞弊检讨书
2015/01/01 职场文书
晚会开幕词
2015/01/28 职场文书
小学生作文之《压岁钱的烦恼》
2019/09/27 职场文书
讨论nginx location 顺序问题
2022/05/30 Servers
Go微服务项目配置文件的定义和读取示例详解
2022/06/21 Golang