运行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 相关文章推荐
Div Select挡住的解决办法
Aug 07 Javascript
js实现数组转换成json
Jun 26 Javascript
js实现的简洁网页滑动tab菜单效果代码
Aug 24 Javascript
DeviceOne 让你一见钟情的App快速开发平台
Feb 17 Javascript
bootstrap模态框实现拖拽效果
Dec 14 Javascript
jQuery extend()详解及简单实例
May 06 jQuery
echarts设置图例颜色和地图底色的方法实例
Aug 01 Javascript
Vue入门之数量加减运算操作示例
Dec 11 Javascript
JS使用iView的Dropdown实现一个右键菜单
May 06 Javascript
详解vue-template-admin三级路由无法缓存的解决方案
Mar 10 Javascript
el-table树形表格表单验证(列表生成序号)
May 31 Javascript
在antd4.0中Form使用initialValue操作
Nov 02 Javascript
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
将文件夹压缩成zip文件的php代码
2009/12/14 PHP
PHP 创建标签云函数代码
2010/05/26 PHP
PHP数组的交集array_intersect(),array_intersect_assoc(),array_inter_key()函数的小问题
2011/05/29 PHP
PHP转换IP地址到真实地址的方法详解
2013/06/09 PHP
thinkPHP简单实现多个子查询语句的方法
2016/12/05 PHP
php微信公众号开发(4)php实现自定义关键字回复
2016/12/15 PHP
PHP中PDO事务处理操作示例
2018/05/02 PHP
javascript高亮效果的二种实现方法
2008/09/14 Javascript
用js实现控件的隐藏及style.visibility的使用
2013/06/14 Javascript
js setTimeout 参数传递使用介绍
2013/08/13 Javascript
javascript中的if语句使用介绍
2013/11/20 Javascript
js实现点击图片将图片地址复制到粘贴板的方法
2015/02/16 Javascript
JS实现CheckBox复选框全选、不选或全不选功能
2020/07/28 Javascript
基于jquery编写分页插件
2016/03/07 Javascript
详解jQuery中的deferred对象的使用(一)
2016/05/27 Javascript
jquery 实现回车登录详解及实例代码
2016/10/23 Javascript
关于JavaScript中事件绑定的方法总结
2016/10/26 Javascript
json字符串传到前台input的方法
2018/08/06 Javascript
JS实现的RC4加密算法示例
2018/08/16 Javascript
深入学习js函数的隐式参数 arguments 和 this
2019/06/24 Javascript
webpack + vue 打包生成公共配置文件(域名) 方便动态修改
2019/08/29 Javascript
vscode中的vue项目报错Property ‘xxx‘ does not exist on type ‘CombinedVueInstance<{ readyOnly...Vetur(2339)
2020/09/11 Javascript
基于p5.js 2D图像接口的扩展(交互实现)
2020/11/30 Javascript
js基于canvas实现时钟组件
2021/02/07 Javascript
[43:41]OG vs Newbee 2019国际邀请赛淘汰赛 胜者组 BO3 第一场 8.21.mp4
2020/07/19 DOTA
python发送邮件示例(支持中文邮件标题)
2014/02/16 Python
Python基于PycURL自动处理cookie的方法
2015/07/25 Python
通过PHP与Python代码对比的语法差异详解
2019/07/10 Python
Python 日期区间处理 (本周本月上周上月...)
2019/08/08 Python
python框架flask表单实现详解
2019/11/04 Python
windows环境中利用celery实现简单任务队列过程解析
2019/11/29 Python
pycharm激活方法到2099年(激活流程)
2020/09/22 Python
国际会议邀请函范文
2014/01/16 职场文书
热爱祖国的演讲稿
2014/05/04 职场文书
如何用JavaScipt测网速
2021/05/09 Javascript
使用springMVC所需要的pom配置
2021/09/15 Java/Android