运行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编程起步(第三课)
Feb 27 Javascript
javascript 命名空间以提高代码重用性
Nov 13 Javascript
HTA版JSMin(省略修饰语若干)基于javascript语言编写
Dec 24 Javascript
javascript 弹出窗口中是否显示地址栏的实现代码
Apr 14 Javascript
javascript 保存文件到本地实现方法
Nov 29 Javascript
js控制页面控件隐藏显示的两种方法介绍
Oct 09 Javascript
JS对象转换为Jquery对象示例
Jan 26 Javascript
RequireJS入门一之实现第一个例子
Sep 30 Javascript
jQuery实现form表单基于ajax无刷新提交方法详解
Dec 08 Javascript
vue Element-ui input 远程搜索与修改建议显示模版的示例代码
Oct 19 Javascript
Vue中父子组件通讯之todolist组件功能开发
May 21 Javascript
vue-cli4使用全局less文件中的变量配置操作
Oct 21 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
PHP整合PayPal支付
2015/06/11 PHP
prototype 1.5相关知识及他人笔记
2006/12/16 Javascript
JavaScript prototype属性使用说明
2010/05/13 Javascript
eval的两组性能测试数据
2012/08/17 Javascript
JavaScript自动设置IFrame高度的小例子
2013/06/08 Javascript
Jquery通过Ajax访问XML数据的小例子
2013/11/18 Javascript
js模拟hashtable的简单实例
2014/03/06 Javascript
基于JavaScript实现简单的随机抽奖小程序
2016/01/05 Javascript
微信支付 JS API支付接口详解
2016/07/11 Javascript
Javascript中字符串相关常用的使用方法总结
2017/03/13 Javascript
weex slider实现滑动底部导航功能
2017/08/28 Javascript
详解使用 Node.js 开发简单的脚手架工具
2018/06/08 Javascript
vue-router判断页面未登录自动跳转到登录页的方法示例
2018/11/04 Javascript
Vue 引入AMap高德地图的实现代码
2019/04/29 Javascript
微信小程序登录数据解密及状态维持实例详解
2019/05/06 Javascript
[02:41]DOTA2英雄基础教程 冥魂大帝
2014/01/16 DOTA
CentOS中使用virtualenv搭建python3环境
2015/06/08 Python
Python实现LRU算法的2种方法
2015/06/24 Python
Python的Twisted框架中使用Deferred对象来管理回调函数
2016/05/25 Python
django使用图片延时加载引起后台404错误
2017/04/18 Python
python下10个简单实例代码
2017/11/15 Python
Python数据结构与算法之图的最短路径(Dijkstra算法)完整实例
2017/12/12 Python
详解Python中的四种队列
2018/05/21 Python
浅谈Python基础—判断和循环
2019/03/22 Python
解决pycharm运行程序出现卡住scanning files to index索引的问题
2019/06/27 Python
django ModelForm修改显示缩略图 imagefield类型的实例
2019/07/28 Python
python判断无向图环是否存在的示例
2019/11/22 Python
python实现批量命名照片
2020/06/18 Python
详解Python GUI编程之PyQt5入门到实战
2020/12/10 Python
Html5 new XMLHttpRequest()监听附件上传进度
2021/01/14 HTML / CSS
美国韩国化妆品和护肤品购物网站:Beautytap
2018/07/29 全球购物
工商管理本科毕业生求职信范文
2013/10/05 职场文书
大学生党员批评与自我批评
2014/09/28 职场文书
redis哨兵常用命令和监控示例详解
2021/05/27 Redis
Elasticsearch 批量操作
2022/04/19 Python
Java 写一个简单的图书管理系统
2022/04/26 Java/Android