运行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 相关文章推荐
js获取当前路径的简单示例代码
Jan 08 Javascript
js设置控件的隐藏与显示的两种方法
Aug 21 Javascript
node.js中使用socket.io制作命名空间
Dec 15 Javascript
jQuery实现购物车表单自动结算效果实例
Aug 10 Javascript
JavaScript必知必会(六) delete in instanceof
Jun 08 Javascript
jquery.tableSort.js表格排序插件使用方法详解
Aug 12 Javascript
浅谈JavaScript中的apply/call/bind和this的使用
Feb 26 Javascript
关于jQuery EasyUI 中刷新Tab选项卡后一个页面变形的解决方法
Mar 02 Javascript
JS匿名函数和匿名自执行函数概念与用法分析
Mar 16 Javascript
vue 刷新之后 嵌套路由不变 重新渲染页面的方法
Sep 13 Javascript
基于vue-cli搭建多模块且各模块独立打包的项目
Jun 12 Javascript
vue中echarts图表大小适应窗口大小且不需要刷新案例
Jul 19 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
自动跳转中英文页面
2006/10/09 PHP
谨慎使用PHP的引用原因分析
2012/09/06 PHP
微信小程序 消息推送php服务器验证实例详解
2017/03/30 PHP
Laravel框架路由和控制器的绑定操作方法
2018/06/12 PHP
Mootools 1.2教程 函数
2009/09/15 Javascript
Exitjs获取DataView中图片文件名
2009/11/26 Javascript
jQuery 连续列表实现代码
2009/12/21 Javascript
JS使用replace()方法和正则表达式进行字符串的搜索与替换实例
2014/04/10 Javascript
浅谈javascript中字符串String与数组Array
2014/12/31 Javascript
jquery实现仿JqueryUi可拖动的DIV实例
2015/07/31 Javascript
JavaScript动态创建form表单并提交的实现方法
2015/12/10 Javascript
JavaScript判断用户名和密码不能为空的实现代码
2016/05/16 Javascript
Javascript中的arguments对象
2016/06/20 Javascript
JavaScript登录验证码的实现
2016/10/27 Javascript
js学习总结之DOM2兼容处理重复问题的解决方法
2017/07/27 Javascript
JS跨域请求的问题解析
2018/12/03 Javascript
JS实现的图片选择顺序切换和循环切换功能示例【测试可用】
2018/12/28 Javascript
Javascript ParentNode和ChildNode接口原理解析
2020/03/16 Javascript
python获取本地计算机名字的方法
2015/04/29 Python
python基于右递归解决八皇后问题的方法
2015/05/25 Python
python中将函数赋值给变量时需要注意的一些问题
2017/08/18 Python
Python实现从log日志中提取ip的方法【正则提取】
2018/03/31 Python
HTML5之SVG 2D入门7—SVG元素的重用与引用
2013/01/30 HTML / CSS
介绍一下XMLHttpRequest对象
2012/02/12 面试题
通信工程毕业生自荐信
2013/11/01 职场文书
教师业务培训方案
2014/05/01 职场文书
地球一小时宣传标语
2014/06/24 职场文书
绿色环保家庭事迹材料
2014/08/31 职场文书
教师个人事迹材料
2014/12/17 职场文书
2015年入党决心书
2015/02/05 职场文书
学校办公室主任岗位职责
2015/04/01 职场文书
大学社团活动总结怎么写
2019/06/21 职场文书
导游词之山东八仙过海景区
2019/11/11 职场文书
Nginx虚拟主机的配置步骤过程全解
2022/03/31 Servers
Python tensorflow卷积神经Inception V3网络结构
2022/05/06 Python
delete in子查询不走索引问题分析
2022/07/07 MySQL