运行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 相关文章推荐
关于jquery动态增减控件的一些想法和小插件
Aug 01 Javascript
js在数组中删除重复的元素自保留一个(两种实现思路)
Aug 22 Javascript
jQuery调取jSon数据并展示的方法
Jan 29 Javascript
JavaScript和jquery获取父级元素、子级元素、兄弟元素的方法
Jun 05 Javascript
全面了解javascript中的错误处理机制
Jul 18 Javascript
文件上传,iframe跨域数据提交的实现
Nov 18 Javascript
two.js之实现动画效果示例
Nov 06 Javascript
vue-router相关基础知识及工作原理
Mar 16 Javascript
实现Vue的markdown文档可以在线运行的方法示例
Dec 11 Javascript
JS开发常用工具函数(小结)
Jul 04 Javascript
Vue-cli项目部署到Nginx服务器的方法
Nov 01 Javascript
2分钟实现一个Vue实时直播系统的示例代码
Jun 05 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 - Html Transfer Code
2006/10/09 PHP
JS链式调用的实现方法
2013/03/07 Javascript
javascript实现复选框选中属性
2015/03/25 Javascript
jquery获取url参数及url加参数的方法
2015/10/26 Javascript
json定义及jquery操作json的方法
2016/09/29 Javascript
jQuey将序列化对象在前台显示地实现代码(方法总结)
2016/12/13 Javascript
jQuery设置和获取select、checkbox、radio的选中值方法
2017/01/01 Javascript
javascript滚轮事件基础实例讲解(37)
2017/02/14 Javascript
基于jquery实现二级联动效果
2017/03/30 jQuery
jQuery UI Grid 模态框中的表格实例代码
2017/04/01 jQuery
用nodejs实现json和jsonp服务的方法
2017/08/25 NodeJs
javascript 面向对象实战思想分享
2017/09/07 Javascript
Vue.js 点击按钮显示/隐藏内容的实例代码
2018/02/08 Javascript
bootstrap实现点击删除按钮弹出确认框的实例代码
2018/08/16 Javascript
JS对象和字符串之间互换操作实例分析
2019/02/02 Javascript
Layui数据表格判断编辑输入的值,是否为我需要的类型详解
2019/10/26 Javascript
js生成1到100的随机数最简单的实现方法
2020/02/07 Javascript
vue 使用post/get 下载导出文件操作
2020/08/07 Javascript
nodejs使用Sequelize框架操作数据库的实现
2020/10/21 NodeJs
用python + openpyxl处理excel2007文档思路以及心得
2014/07/14 Python
Python显示进度条的方法
2014/09/20 Python
Python中pygame安装方法图文详解
2015/11/11 Python
python数据类型_元组、字典常用操作方法(介绍)
2017/05/30 Python
用python打印菱形的实操方法和代码
2019/06/25 Python
python3 中的字符串(单引号、双引号、三引号)以及字符串与数字的运算
2019/07/18 Python
Python爬虫自动化爬取b站实时弹幕实例方法
2021/01/26 Python
纯CSS3实现带动画效果导航菜单无需js
2013/09/27 HTML / CSS
世界顶级足球门票网站:Live Football Tickets
2017/10/14 全球购物
JackJones官方旗舰店:杰克琼斯男装
2018/03/27 全球购物
周仰杰(JIMMY CHOO)英国官方网站:闻名世界的鞋子品牌
2018/10/28 全球购物
妈妈活动方案
2014/08/15 职场文书
四大名著读书笔记
2015/06/25 职场文书
《学会生存》读后感3篇
2019/12/09 职场文书
使用Ajax实现进度条的绘制
2022/04/07 Javascript
JavaScript中10个Reduce常用场景技巧
2022/06/21 Javascript
MySQL的意向共享锁、意向排它锁和死锁
2022/07/15 MySQL