如何从 Node.js 中的 URL 请求

Posted

技术标签:

【中文标题】如何从 Node.js 中的 URL 请求【英文标题】:how to require from URL in Node.js 【发布时间】:2011-12-10 04:45:33 【问题描述】:

是否有标准方法要求位于某个 URL(而不是本地文件系统)的 Node 模块?

类似:

require('http://example.com/nodejsmodules/myModule.js');

目前,我只是将文件提取到临时文件中,并要求这样做。

【问题讨论】:

您意识到依赖远程 HTTP 服务器始终如一地为您提供源代码是愚蠢的。然后信任远程HTTP服务器不给你不安全的代码简直太荒谬了 如果有的话,您应该提供一些机制来防止中间人攻击或通过 https 获取所有文件,这会使获取速度变慢。 一点也不傻。它允许您构建具有其他人可以利用的核心功能的骨架 @Raynos 你说这很傻,但这就是 Ryan Dahl 选择为 Deno 做的事情 【参考方案1】:

您可以使用http.get 方法获取模块并使用vm 模块方法runInThisContext 和runInNewContext 在沙箱中执行它。

例子

var http = require('http')
  , vm = require('vm')
  , concat = require('concat-stream'); // this is just a helper to receive the
                                       // http payload in a single callback
                                       // see https://www.npmjs.com/package/concat-stream

http.get(
    host: 'example.com', 
    port: 80, 
    path: '/hello.js'
  , 
  function(res) 
    res.setEncoding('utf8');
    res.pipe(concat( encoding: 'string' , function(remoteSrc) 
      vm.runInThisContext(remoteSrc, 'remote_modules/hello.js');
    ));
);

IMO,在没有替代方案的情况下,在服务器应用程序运行时内执行远程代码可能是合理的。并且仅当您信任远程服务和网络之间的网络。

【讨论】:

我赞成这些信息,但我希望您不要仅仅说“这是非常糟糕的做法”,而是要费心解释为什么 1.也许代码可以通过广泛信任的 CDN 中的 HTTPS 或通过受信任的合作伙伴获得。 2. 也许代码可以通过 HTTPS 在远程内部位置获得。 3. 可能代码在内部被打包和分发,导致访问最终执行的 FS 被阻塞,因此无法使用 npm。这些只是我的想法;也许它们不是很好的理由,但它们是理由。无论如何,我的观点很简单,解释观点的理由比将其标记为“不良做法”而不再多说更有用。你现在的评论就是这样! 在这个时代,HTTP-GET 应该被认为是引用/打开文件的一种完全有效的方法。暗示文件在某种程度上更安全​​,因为您首先将它们wgetted 到您的本地硬盘驱动器,这非常类似于通过默默无闻的安全性。一个文件的可信度不应仅仅通过您是通过 FILE:// 还是 HTTPS 访问它来衡量?:// 所以,在生产应用程序中不需要来自“github”的任何东西;) 感谢您的回答。至于“执行远程代码”;这些天来,大多数节点应用程序都使用npm install 进行初始化,该npm install 来自网络。这与行动在精神上寻求做的有什么不同?总的来说,这个想法没有什么不好的做法,但是任何事情都可以做得很差;制造安全风险。我希望做这件事来管理我的配置脚本(它不仅仅是 json,因为它需要更多的智能)并且我有旋转密钥来保护请求,但无论如何没人会知道应用程序(更安全比 npm 模块)。【参考方案2】:

您可以覆盖 .js 文件的默认要求处理程序:

require.extensions['.js'] = function (module, filename) 
    // ...

您可能需要查看better-require,因为它对许多文件格式几乎都执行此操作。 (我写的)

【讨论】:

遗憾的是 require.extensions 已被弃用...【参考方案3】:

如果你想要更多类似require 的东西,你可以这样做:

var http = require('http')
  , vm = require('vm')
  , concat = require('concat-stream') 
  , async = require('async'); 

function http_require(url, callback) 
  http.get(url, function(res) 
    // console.log('fetching: ' + url)
    res.setEncoding('utf8');
    res.pipe(concat(encoding: 'string', function(data) 
      callback(null, vm.runInThisContext(data));
    ));
  )


urls = [
  'http://example.com/nodejsmodules/myModule1.js',
  'http://example.com/nodejsmodules/myModule2.js',
  'http://example.com/nodejsmodules/myModule3.js',
]

async.map(urls, http_require, function(err, results) 
  // `results` is an array of values returned by `runInThisContext`
  // the rest of your program logic
);

【讨论】:

【参考方案4】:

0 依赖版本(需要 node 6+,你可以简单地把它改回 ES5)

const http = require('http'), vm = require('vm');

['http://example.com/nodejsmodules/myModule.js'].forEach(url => 
    http.get(url, res => 
        if (res.statusCode === 200 && /^text\/javascript/.test(res.headers['content-type'])) 
            let rawData = '';
            res.setEncoding('utf8');
            res.on('data', chunk =>  rawData += chunk; );
            res.on('end', () =>  vm.runInThisContext(rawData, url); );
        
    );
);

还是异步的版本,如果是同步加载的话,需要sync http request modulefor example

【讨论】:

我认为你应该在if (res.statusCode === 200 && /^application\/javascript/.test(res.headers['content-type']))中使用'application/javascript'而不是'text/javascript'【参考方案5】:

  const localeSrc = 'https://www.trip.com/m/i18n/100012631/zh-HK.js';
  const http = require('http');
  const vm = require('vm');
  const concat = require('concat-stream');
  http.get(
    localeSrc,
    res => 
      res.setEncoding('utf8');
      res.pipe(
        concat( encoding: 'string' , remoteSrc => 
          let context = ;
          const script = new vm.Script(remoteSrc);
          script.runInNewContext(context);
          console.log(context);
        ),
      );
    ,
    err => 
      console.log('err', err);
    ,
  );

【讨论】:

嗨!欢迎来到 SO。发布答案时,请务必说明您的解决方案如何工作以及如何解决问题。【参考方案6】:

先安装模块:

npm install require-from-url

然后放入你的文件:

var requireFromUrl = require('require-from-url/sync');
requireFromUrl("http://example.com/nodejsmodules/myModule.js");

【讨论】:

如果脚本以 gzip 格式提供,这似乎不起作用 哇,这完全符合预期。谢谢 不幸的是,这种方法不适用于 AWS (Lambda) 等环境,因为在内部它会启动节点进程来获取 url 内容,而这在这样的环境中是不允许的。

以上是关于如何从 Node.js 中的 URL 请求的主要内容,如果未能解决你的问题,请参考以下文章

如何从Node.js中的URL加载外部js脚本

如何从 Node.js 中的 API 请求创建响应流?

Node.JS:如何将变量传递给异步回调? [复制]

如何将 JQuery AJAX 请求中的数据发送到 Node.js 服务器

如何使用 node.js 请求主题标签顶部推文的 url? [关闭]

如何从 node.js Express 发送 POST 请求?