运行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 面试题随笔
Mar 31 Javascript
jQuery中mouseover事件用法实例
Dec 26 Javascript
jquery简单倒计时实现方法
Dec 18 Javascript
jquery hover 不停闪动问题的解决方法(亦为stop()的使用)
Feb 10 Javascript
mint-ui 时间插件使用及获取选择值的方法
Feb 09 Javascript
如何从零开始手写Koa2框架
Mar 22 Javascript
利用Vue-draggable组件实现Vue项目中表格内容的拖拽排序
Jun 07 Javascript
解决layer.confirm快速点击会重复触发事件的问题
Sep 23 Javascript
基于Nuxt.js项目的服务端性能优化与错误检测(容错处理)
Oct 23 Javascript
javascript设计模式 ? 抽象工厂模式原理与应用实例分析
Apr 09 Javascript
在vue中封装方法以及多处引用该方法详解
Aug 14 Javascript
Electron+vue从零开始打造一个本地播放器的方法示例
Oct 27 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
介绍几个array库的新函数 php
2006/12/29 PHP
PHP实现微信网页授权开发教程
2016/01/19 PHP
laravel 数据迁移与 Eloquent ORM的实现方法
2019/04/12 PHP
PHP+fiddler抓包采集微信文章阅读数点赞数的思路详解
2019/12/20 PHP
php设计模式之原型模式分析【星际争霸游戏案例】
2020/03/23 PHP
js removeChild 障眼法 可能出现的错误
2009/10/06 Javascript
ExtJS4 组件化编程,动态加载,面向对象,Direct
2011/05/12 Javascript
ie7+背景透明文字不透明超级简单的实现方法
2014/01/17 Javascript
jQuery .tmpl() 用法示例介绍
2014/08/21 Javascript
js实现宇宙星空背景效果的方法
2015/03/03 Javascript
js实现的在线调色板功能完整实例
2016/12/21 Javascript
jQuery插件FusionCharts绘制的3D饼状图效果实例【附demo源码下载】
2017/03/03 Javascript
解决npm安装Electron缓慢网络超时导致失败的问题
2018/02/06 Javascript
如何在js代码中消灭for循环实例详解
2018/07/29 Javascript
基于JS开发微信网页录音功能的实例代码
2019/04/30 Javascript
vue v-for 使用问题整理小结
2019/08/04 Javascript
详解node登录接口之密码错误限制次数(含代码)
2019/10/25 Javascript
JS中箭头函数与this的写法和理解
2021/01/14 Javascript
python计算N天之后日期的方法
2015/03/31 Python
Python实现的将文件每一列写入列表功能示例【测试可用】
2018/03/19 Python
python设定并获取socket超时时间的方法
2019/01/12 Python
python实现杨氏矩阵查找
2019/03/02 Python
Django框架自定义session处理操作示例
2019/05/27 Python
浅谈keras通过model.fit_generator训练模型(节省内存)
2020/06/17 Python
pandas之分组groupby()的使用整理与总结
2020/06/18 Python
python 如何设置守护进程
2020/10/29 Python
python基于selenium爬取斗鱼弹幕
2021/02/20 Python
canvas如何实现多张图片编辑的图片编辑器
2020/03/10 HTML / CSS
德国网上宠物店:Zoobio
2018/05/23 全球购物
广州某公司软件工程师面试题
2014/12/22 面试题
Lucene推荐的分页方式是什么?
2015/12/07 面试题
安全生产专项整治方案
2014/05/06 职场文书
2015年学校减负工作总结
2015/05/19 职场文书
深度学习详解之初试机器学习
2021/04/14 Python
关于 Python json中load和loads区别
2021/11/07 Python
python中mongodb包操作数据库
2022/04/19 Python