Gatling:对隐藏在 HTML 响应中的某些 JSON 执行检查
Gatling: execute a check on some JSON hidden inside an HTML response
在 Gatling 中,我想对 HTML 响应中包含的一些 JSON 进行检查,如下所示:
<!doctype html>
<html lang="fr">
<head>
<script>
var documentLoaded = performance.now();
</script>
<link rel="stylesheet" href="/styles/main.f14d8fab5a7e.css">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<link rel="preconnect" href="https://www.gstatic.com">
<title data-react-helmet="true">Asus Discount</title>
<meta data-react-helmet="true" name="description" content="Asus discount”/><meta data-react-helmet="true" name="keywords" content="Asus"/>
</head>
<body>
<div>Some content</div>
<script>
var parseStart = performance.now();
</script>
<script>
window.__INITIAL_STATE__ = {some JSON}; <!-- This is what I need -->
window.__ENV_VARIABLES__ = {some other JSON};
window.renderTime = '76';
window.fetchTime = '349';
</script>
<script type="text/javascript" charset="utf-8" src="/vendor.e33d9940372.js"></script>
<script type="application/ld+json" src="/schema.fr.json"></script>
</body>
</html>
我的实际解决方案(有效)如下所示:
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder ={
exec(
http(requestName)
.get(link)
.check(regex("""window[.]__INITIAL_STATE__ = ([^;]+)""").find.transform(s => parseSToProdList(s)).saveAs("prod_list")
)
)
doIf("${prod_list.size()}" == 0){
exec{session => session.markAsFailed}
}
}
def parseSToProdList(jsonString: String): Seq[String] ={
val jsonMap = jsonStrToMap(jsonString)
val buffer = mutable.Buffer.empty[String]
jsonMap("products").asInstanceOf[Map[String, Any]].foreach{f =>
if(f._2.asInstanceOf[Map[String, Any]].keySet.exists(_ == "code"))
buffer.append(f._2.asInstanceOf[Map[String, Any]]("code").asInstanceOf[String])
}
buffer.toSeq
}
def jsonStrToMap(jsonStr: String): Map[String, Any] = {
implicit val formats = org.json4s.DefaultFormats
parse(jsonStr).extract[Map[String, Any]]
}
但是,这个解决方案有几个缺点:
- 只要找到正则表达式,检查就会成功,并且不关心 JSON 中是否有任何产品 -> 我必须稍后手动检查它;
- 拥有一个提取所需数据的函数比我可以使用 Json 路径表达式(如“$.products.*.code”)更难维护,它可以存储在一个集中的路径文件中易于维护;
- 这是我必须使用转换来检查请求的 JSON 的唯一地方,这使得它更难阅读和理解。
我想要实现的是看起来有点像这样的东西:
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder ={
exec(
http(requestName)
.get(link)
.check(jsonPath("""$.products.*.code""").findAll.saveAs("prod_list")
)
或
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder ={
exec(
http(requestName)
.get(link)
.check(jsonpJsonPath("""$.products.*.code""").findAll.saveAs("prod_list")
)
当然,jsonPath 不起作用,因为大多数答案是 HTML。 jsonpJsonPath 也不起作用,因为响应中有多个 Json 字符串。
关于如何更有效(更好地)执行此操作同时避免某些 HTML 上的正则表达式的任何好的输入?提前致谢
因此,经过一番挖掘,我找到了使用“.transformResponse”的变通方法,以便在实际检查之前提取字符串,为其提供一个可在 Json 中解析的默认值。然后,为了确保我们确实找到了正则表达式,我们确保它不是我们的默认值:
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder = {
exec(
http(requestName)
.get(link)
.transformResponse{(session, response) =>
response.copy(body = new StringResponseBody(
(for(m <- """window[.]__INITIAL_STATE__ = ([^;]+)""".r
.findFirstMatchIn(response.body.string)
) yield m.group(1)
).getOrElse("""{"error":"chain not found"}"""),
UTF_8
)
)
}
.check(bodyString.not("""{"error":"chain not found"}"""))
.check(jsonPath("""$.products.*.code""").findAll.saveAs("prod_list")
)
)
}
在 Gatling 中,我想对 HTML 响应中包含的一些 JSON 进行检查,如下所示:
<!doctype html>
<html lang="fr">
<head>
<script>
var documentLoaded = performance.now();
</script>
<link rel="stylesheet" href="/styles/main.f14d8fab5a7e.css">
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="manifest" href="/manifest.json">
<link rel="preconnect" href="https://www.gstatic.com">
<title data-react-helmet="true">Asus Discount</title>
<meta data-react-helmet="true" name="description" content="Asus discount”/><meta data-react-helmet="true" name="keywords" content="Asus"/>
</head>
<body>
<div>Some content</div>
<script>
var parseStart = performance.now();
</script>
<script>
window.__INITIAL_STATE__ = {some JSON}; <!-- This is what I need -->
window.__ENV_VARIABLES__ = {some other JSON};
window.renderTime = '76';
window.fetchTime = '349';
</script>
<script type="text/javascript" charset="utf-8" src="/vendor.e33d9940372.js"></script>
<script type="application/ld+json" src="/schema.fr.json"></script>
</body>
</html>
我的实际解决方案(有效)如下所示:
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder ={
exec(
http(requestName)
.get(link)
.check(regex("""window[.]__INITIAL_STATE__ = ([^;]+)""").find.transform(s => parseSToProdList(s)).saveAs("prod_list")
)
)
doIf("${prod_list.size()}" == 0){
exec{session => session.markAsFailed}
}
}
def parseSToProdList(jsonString: String): Seq[String] ={
val jsonMap = jsonStrToMap(jsonString)
val buffer = mutable.Buffer.empty[String]
jsonMap("products").asInstanceOf[Map[String, Any]].foreach{f =>
if(f._2.asInstanceOf[Map[String, Any]].keySet.exists(_ == "code"))
buffer.append(f._2.asInstanceOf[Map[String, Any]]("code").asInstanceOf[String])
}
buffer.toSeq
}
def jsonStrToMap(jsonStr: String): Map[String, Any] = {
implicit val formats = org.json4s.DefaultFormats
parse(jsonStr).extract[Map[String, Any]]
}
但是,这个解决方案有几个缺点:
- 只要找到正则表达式,检查就会成功,并且不关心 JSON 中是否有任何产品 -> 我必须稍后手动检查它;
- 拥有一个提取所需数据的函数比我可以使用 Json 路径表达式(如“$.products.*.code”)更难维护,它可以存储在一个集中的路径文件中易于维护;
- 这是我必须使用转换来检查请求的 JSON 的唯一地方,这使得它更难阅读和理解。
我想要实现的是看起来有点像这样的东西:
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder ={
exec(
http(requestName)
.get(link)
.check(jsonPath("""$.products.*.code""").findAll.saveAs("prod_list")
)
或
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder ={
exec(
http(requestName)
.get(link)
.check(jsonpJsonPath("""$.products.*.code""").findAll.saveAs("prod_list")
)
当然,jsonPath 不起作用,因为大多数答案是 HTML。 jsonpJsonPath 也不起作用,因为响应中有多个 Json 字符串。
关于如何更有效(更好地)执行此操作同时避免某些 HTML 上的正则表达式的任何好的输入?提前致谢
因此,经过一番挖掘,我找到了使用“.transformResponse”的变通方法,以便在实际检查之前提取字符串,为其提供一个可在 Json 中解析的默认值。然后,为了确保我们确实找到了正则表达式,我们确保它不是我们的默认值:
def loadPageJsonInHTML(requestName: String, link: String): ChainBuilder = {
exec(
http(requestName)
.get(link)
.transformResponse{(session, response) =>
response.copy(body = new StringResponseBody(
(for(m <- """window[.]__INITIAL_STATE__ = ([^;]+)""".r
.findFirstMatchIn(response.body.string)
) yield m.group(1)
).getOrElse("""{"error":"chain not found"}"""),
UTF_8
)
)
}
.check(bodyString.not("""{"error":"chain not found"}"""))
.check(jsonPath("""$.products.*.code""").findAll.saveAs("prod_list")
)
)
}