如何在 Javascript/Node.js Azure 函数中使用需要 DOM 和 window 的导入脚本
How to use imported scripts needing DOM and window in Javascript/Node.js Azure Function
我正在尝试在 Azure 函数中使用外部 javascript(javascript/node.js 风格)。由于外部javascript(我需要使用kendo)依赖于一个DOM和一个window,我尝试使用JSDOM(但我没有如果有一些 better/easier 替代方案,则不必使用 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
};
};
当 运行 这个 Azure 函数时,它应该包含 jquery 和 kendo 并创建一个包含 "Hello World" 的 div。但这不起作用。
创建 Hello World 的脚本 div 只有在我注释掉其中之一时才会执行
+ '<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>'
或
resources: "usable"
但是我不能使用 jquery 和 kendo。
根据这个 jsdom documentation 参数资源:"usable" 应该可以解决问题,但由于某些原因它不起作用。
我正在使用 "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
};
};
这是向 header 添加脚本标记,但 innerHTML 是 "undefined" 而不是 jquery 字符串。
在 JSDOM 之外(请参阅注释掉的行)记录 jqueryString 按预期显示 jquery.js 的内容。但是在 JSDOM 内部,该变量似乎不可用。如何将 jqueryString 传递给 JSDOM?
我仔细检查了jsdom
节点包,然后我发现Azure Web App sandbox
的Loading subresources
required a headless browser like headless Chrome via karma
. However, due to the Win32k.sys (User32/GDI32) Restrictions
特性,在[=的Azure应用服务上永远无法正常工作。 29=],因为无头浏览器需要 GDI
支持。
所以我建议您需要通过 Azure Functions Core Tools
as the offical document Create your first function hosted on Linux using Core Tools and the Azure CLI (preview)
所说的重建将函数迁移到 Linux 的 Azure Functions(它基于 Docker 容器)。或者您必须使用 Windows,Azure Windows VM 将是您的最佳选择。
希望对您有所帮助。
在@PeterPan 的帮助下,我设法使用 Docker based Azure function with Puppeteer.
解决了我的问题
初始化 Docker 基于 FunctionApp:
func init MyFunctionProj --docker
(select -> 节点 -> javascript)
编辑 MyFunctionProj\Dockerfile 以使用 estruyf/azure-function-node-puppeteer 和 fs :
FROM estruyf/azure-function-node-puppeteer
RUN npm i fs
COPY . /home/site/wwwroot
将 fs 添加到 MyFunctionProj\package 中的依赖项。json:
"dependencies": {
"fs": "0.0.1-security"
}
创建 HttpTrigger 函数:
cd MyFunctionProj
func new --name MyHttpTrigger --template "HttpTrigger"
授予函数访问权限:
在MyFunctionProj\MyHttpTrigger\function中设置"authLevel": "anonymous"
。json(原因见。)
创建 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>
编辑MyFunctionProj\MyHttpTrigger\index.js中的函数逻辑(基于this):
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图像:
docker build --tag <docker-id>/mydockerimage:v1.0.0 .
运行 docker 图片:
docker run -p 8080:80 -it <docker-ID>/mydockerimage:v1.0.0
在浏览器中测试本地函数图像:
http://localhost:8080/api/MyHttpTrigger
将图像推送到 Docker 集线器:
docker login --username <docker-id>
docker push <docker-id>/mydockerimage:v1.0.0
联机创建新的 Azure 函数并使用来自 Docker Hub 的图像,如 here 所述。
我正在尝试在 Azure 函数中使用外部 javascript(javascript/node.js 风格)。由于外部javascript(我需要使用kendo)依赖于一个DOM和一个window,我尝试使用JSDOM(但我没有如果有一些 better/easier 替代方案,则不必使用 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
};
};
当 运行 这个 Azure 函数时,它应该包含 jquery 和 kendo 并创建一个包含 "Hello World" 的 div。但这不起作用。
创建 Hello World 的脚本 div 只有在我注释掉其中之一时才会执行
+ '<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>'
或
resources: "usable"
但是我不能使用 jquery 和 kendo。
根据这个 jsdom documentation 参数资源:"usable" 应该可以解决问题,但由于某些原因它不起作用。
我正在使用 "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
};
};
这是向 header 添加脚本标记,但 innerHTML 是 "undefined" 而不是 jquery 字符串。 在 JSDOM 之外(请参阅注释掉的行)记录 jqueryString 按预期显示 jquery.js 的内容。但是在 JSDOM 内部,该变量似乎不可用。如何将 jqueryString 传递给 JSDOM?
我仔细检查了jsdom
节点包,然后我发现Azure Web App sandbox
的Loading subresources
required a headless browser like headless Chrome via karma
. However, due to the Win32k.sys (User32/GDI32) Restrictions
特性,在[=的Azure应用服务上永远无法正常工作。 29=],因为无头浏览器需要 GDI
支持。
所以我建议您需要通过 Azure Functions Core Tools
as the offical document Create your first function hosted on Linux using Core Tools and the Azure CLI (preview)
所说的重建将函数迁移到 Linux 的 Azure Functions(它基于 Docker 容器)。或者您必须使用 Windows,Azure Windows VM 将是您的最佳选择。
希望对您有所帮助。
在@PeterPan 的帮助下,我设法使用 Docker based Azure function with Puppeteer.
解决了我的问题初始化 Docker 基于 FunctionApp:
func init MyFunctionProj --docker
(select -> 节点 -> javascript)
编辑 MyFunctionProj\Dockerfile 以使用 estruyf/azure-function-node-puppeteer 和 fs :
FROM estruyf/azure-function-node-puppeteer
RUN npm i fs
COPY . /home/site/wwwroot
将 fs 添加到 MyFunctionProj\package 中的依赖项。json:
"dependencies": {
"fs": "0.0.1-security"
}
创建 HttpTrigger 函数:
cd MyFunctionProj
func new --name MyHttpTrigger --template "HttpTrigger"
授予函数访问权限:
在MyFunctionProj\MyHttpTrigger\function中设置"authLevel": "anonymous"
。json(原因见
创建 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>
编辑MyFunctionProj\MyHttpTrigger\index.js中的函数逻辑(基于this):
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图像:
docker build --tag <docker-id>/mydockerimage:v1.0.0 .
运行 docker 图片:
docker run -p 8080:80 -it <docker-ID>/mydockerimage:v1.0.0
在浏览器中测试本地函数图像:
http://localhost:8080/api/MyHttpTrigger
将图像推送到 Docker 集线器:
docker login --username <docker-id>
docker push <docker-id>/mydockerimage:v1.0.0
联机创建新的 Azure 函数并使用来自 Docker Hub 的图像,如 here 所述。