如何在Javascript/Node.js Azure函数中使用需要DOM和window的导入脚本

如何在Javascript/Node.js Azure函数中使用需要DOM和window的导入脚本,javascript,node.js,azure-functions,jsdom,Javascript,Node.js,Azure Functions,Jsdom,我试图在Azure函数中使用外部javascript,即javascript/node.js。由于我需要使用kendo的外部javascript依赖于DOM和窗口,所以我尝试使用JSDOM,但如果有更好/更简单的替代方法,我不必使用JSDOM 这是我所拥有的一个简化示例: module.exports = async function (context, req) { context.log('JavaScript HTTP trigger function processed a re

我试图在Azure函数中使用外部javascript,即javascript/node.js。由于我需要使用kendo的外部javascript依赖于DOM和窗口,所以我尝试使用JSDOM,但如果有更好/更简单的替代方法,我不必使用JSDOM

这是我所拥有的一个简化示例:

module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    var html = '<!DOCTYPE html>'
    + '<html>'
    +   '<head>'
    +       '<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>'
    +       '<script src="https://kendo.cdn.telerik.com/2019.2.514/js/kendo.all.min.js"></script>'
    +   '</head>'
    +   '<body>'
    +       '<script>'
    +           'var myDiv = document.createElement("div");'        
    +           'myDiv.innerHTML = "Hello World";'
    +           'document.body.appendChild(myDiv);'
    +       '</script>'
    +   '</body>'
    + '</html>'
    ;

    var jsdom = require('jsdom');
    const { JSDOM } = jsdom;

    const dom = new JSDOM(html, {
        runScripts: "dangerously",
        resources: "usable"
    });

    var test = dom.serialize();
    context.log(test);

    context.res = {
        body: test
    };
};
但是我不能使用jquery和剑道

根据这个jsdom,参数resources:available应该可以做到这一点,但由于某些原因,它不起作用

我正在使用jsdom:^15.1.1

更新: 我尝试了一种解决方法,添加如下脚本:

module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    var html = '<!DOCTYPE html>'
    + '<html>'
    +   '<head>'
    +   '</head>'
    +   '<body>'
    +       '<script>'
    +           'var myDiv = document.createElement("div");'        
    +           'myDiv.innerHTML = "Hello World";'
    +           'document.body.appendChild(myDiv);'
    +           'var scriptTag = document.createElement("script");'        
    +           'scriptTag.innerHTML = window.jqueryString;'
    +           'document.head.appendChild(scriptTag);'
    +       '</script>'
    +   '</body>'
    + '</html>'
    ;

    var jsdom = require('jsdom');
    const { JSDOM } = jsdom;

    var fs = require("fs");
    global.jqueryString = fs.readFileSync(__dirname + '//jquery.js').toString();
    //context.log(jqueryString);

    const dom = new JSDOM(html, {
        runScripts: "dangerously"
    });

    var test = dom.serialize();
    context.log(test);


    context.res = {
        body: test
    };
};
这是向标头添加脚本标记,但未定义innerHTML而不是jqueryString。 在JSDOM之外,请参见注释掉的行日志jqueryString,它按预期显示jquery.js的内容。但在JSDOM内部,该变量似乎不可用。如何将jqueryString传递给JSDOM?

我仔细检查了JSDOM节点包,然后发现该功能需要一个类似headless Chrome via的headless浏览器。然而,由于Azure Web App sandbox的缺陷,它永远不会在Azure App Service for Windows上正常工作,因为headless浏览器需要GDI支持

所以我建议您需要将您的函数迁移到Azure Functions for Linux,它是基于Docker容器的,正如官方文档所说的那样,通过重新构建它。或者你必须使用Windows,Azure Windows VM将是你的最佳选择


希望有帮助。

有了@PeterPan的输入,我成功地用With解决了我的问题

基于Init Docker的FunctionApp:

func init MyFunctionProj-docker select->node->javascript

编辑要使用的MyFunctionProj\Dockerfile和fs:

将fs添加到MyFunctionProj\package.json中的依赖项:

"dependencies": {
    "fs": "0.0.1-security"
  }
创建HttpTrigger函数:

cd MyFunctionProj
func new --name MyHttpTrigger --template "HttpTrigger"
授予对功能的访问权限:

在MyFunctionProj\MyHttpTrigger\function.json中设置authLevel:anonymous,原因请参阅

创建MyFunctionProj\MyHttpTrigger\content.html文件:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"/>
    <title>ContentHtml</title>

    <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
    <script src="https://kendo.cdn.telerik.com/2019.2.514/js/kendo.all.min.js"></script>
</head>
<body>

<script>
    $(document).ready(function() {
       var myComputationResult = .... // use included scripts to perform computation
       $("#resultData").html(JSON.stringify(myComputationResult));
    });
</script>
<div id="resultData"></div>
</body>
</html>
构建docker映像:

docker build --tag <docker-id>/mydockerimage:v1.0.0 .
docker run -p 8080:80 -it <docker-ID>/mydockerimage:v1.0.0
将图像推送到Docker Hub:

docker login --username <docker-id>
docker push <docker-id>/mydockerimage:v1.0.0

在线创建新的Azure功能,并使用Docker Hub中的图像,如前所述。

创建Azure功能时,您为其选择了什么操作系统?Windows还是LInux?我保留了默认设置,即Windows。我将尝试让它与LInux Azure函数一起工作,并让您知道我是否让它工作。我创建了上面描述的两个函数变体,即LInux Azure函数,但不幸的是,它们都不工作。Linux变体的行为与Windows变体的行为相同,但有完全相同的错误。@René您是否在映像中预安装了chrome或chrome实例?我使用Puppeter将其用于基于Docker的Linux Azure函数。稍后我会在这里发布完整的答案。谢谢你给我指明了正确的方向@勒内太棒了!在安装木偶演员的同时,还将安装一个无头铬合金。
const puppeteer = require('puppeteer');
const fs = require("fs");

module.exports = async (context, req) => {    
    const browser = await puppeteer.launch({
        args: [
            '--no-sandbox',
            '--disable-setuid-sandbox'
        ]
    });

    var contentHtml = fs.readFileSync(__dirname + '//content.html', 'utf8');

    const page = await browser.newPage();
    await page.goto(`data:text/html,${contentHtml}`, { waitUntil: 'networkidle2' });
    const resultData = await page.evaluate(() => document.querySelector('#resultData').innerHTML);
    await browser.close();

    context.res = {
        // status: 200, /* Defaults to 200 */
        body: `Result: ${resultData}`
    };
};
docker build --tag <docker-id>/mydockerimage:v1.0.0 .
docker run -p 8080:80 -it <docker-ID>/mydockerimage:v1.0.0
http://localhost:8080/api/MyHttpTrigger
docker login --username <docker-id>
docker push <docker-id>/mydockerimage:v1.0.0