【问题标题】:Most secure javascript JSON Inline technique最安全的 javascript JSON 内联技术
【发布时间】:2012-07-31 16:00:45
【问题描述】:

我正在使用 varnish+esi 从 RESTFul API 返回外部 json 内容。 这种技术允许我管理请求和刷新数据,而无需为每个请求使用网络服务器资源。

例如:

<head>
.... 
<script> 
 var data = <esi:include src='apiurl/data'>;
</script>
...

包含 esi varnish 后将返回:

 var data = {attr:1, attr2:'martin'};

这工作正常,但如果 API 返回错误,此技术将生成解析错误。

var data = <html><head><script>...api js here...</script></head><body><h1 ... api html ....

我使用隐藏的 div 解析并捕获错误解决了这个问题:

...
<b id=esi-data style=display:none;><esi:include src='apiurl/data'></b>
<script>
  try{
    var data = $.parseJSON($('#esi-data').html());
  }catch{ alert('manage the error here');}
....

我也尝试过使用脚本类型 text/esi,但浏览器会在脚本标签 (wtf) 中呈现 html,例如:

<script id=esi-data type='text/esi'><esi:include src='apiurl/data'></script>

问题

有什么理由要包装标签并避免浏览器解析它吗?

【问题讨论】:

  • Martin — 您能否包含一个由 API 因错误返回的 HTML 示例?
  • 我真的不知道,但是是这样的:

  • 您的文档是 HTML 还是 XHTML ?
  • @sitifensys: 它是html5 ,另外我正在使用modernizr
  • 天哪。这是一个非常丑陋的情况。一般来说,我会说正确的方法是调用 Ajax,但您似乎不想这样做。如果您非常讨厌标准,我想您可以将内容嵌入iframe 并使用innerHTML 访问它。

标签: javascript json parsing varnish esi


【解决方案1】:

让我扩展一下我在评论中提出的iframe 建议——这和你想的不太一样!

该方法与您已经在做的几乎完全相同,但是您使用 iframe 而不是使用像 div 这样的普通 HTML 元素。

<iframe id="esi-data" src="about:blank"><esi:include src="apiurl/data"></iframe>
var $iframe = $('#esi-data');

try {
    var data = $.parseJSON($iframe.html());
} catch (e) { ... }

$iframe.remove();
#esi-data { display: none; }

这与您的解决方案有何不同?两种方式:

  1. 数据/错误页面对您的访问者真正隐藏。iframe 具有 embedded content 模型,这意味着 &lt;iframe&gt;…&lt;/iframe&gt; 标记中的任何内容都将在 DOM 中完全替换——但您仍然可以使用innerHTML 检索原始内容。

  2. 它是有效的 HTML5... 之类的。 在 HTML5 中,markup inside iframe elements is treated as text。当然,您意味着能够将其解析为片段,并且它意味着只包含短语内容(并且没有 script 元素!),但它本质上只是被验证器——以及浏览器。

  3. 错误页面中的脚本不会运行。内容被解析为文本并在 DOM 中替换为另一个文档 - 没有机会处理任何 script 元素。

Take a look at it in action。如果您注释掉我删除 iframe 元素的行并检查 DOM,您可以确认 HTML 内容被替换为空文档。另请注意,嵌入的 script 标记永远不会运行。

重要提示:如果第三方出于某种原因在他们的错误页面中添加了iframe 元素,这种方法仍然可能会失效。尽管这不太可能,但您可以通过将您的技术与此方法相结合来进一步防止该方法:将 iframe 与隐藏的 div 包围起来,当您完成解析时将其删除。

【讨论】:

  • 问题:是否可以将 iframe 添加到 ?
  • @MartinBorthiry 嗯……这不会是有效的,但如果你不关心这个,试一试并检查 DOM 输出是否有意义(例如 iframe 没有过早关闭head)。就个人而言,出于性能原因,最好在文档body 的末尾加载脚本,这就是我要放的地方。 :)
【解决方案2】:

我再尝试一次。

虽然我相信您已经有了可能的最佳解决方案,但我只能想象您使用一种性能相当低的方法来解决它,即在单独的 HTML 窗口中调用 esi:insert,然后像您一样检索内容在服务器上使用 AJAX。也许类似于this?然后检查您检索到的内容,可能使用 json_decode 并在成功时生成错误 JSON 字符串。

我认为最大的缺点是我认为这会非常消耗,甚至很可能会延迟您的请求,因为调用单独的页面就好像您的服务器自己是客户端一样,被解析然后发回。

老实说,我会坚持你目前的解决方案。

【讨论】:

  • 谢谢,但是我该如何处理引号,如果响应有一个“我会得到一个解析错误。我不能改变API转义方法。
  • @MartinBorthiry 好点。无论如何,如果我是你,我实际上只是简单地将你之前检索数据的尝试包装在一个 try-catch 中。我们知道的一件事是:如果成功,我们就有一个有效的 JSON。如果它失败了,catch 应该处理它,不是吗?
  • try/catch 不能捕获解析错误,试试这个:
  • @MartinBorthiry 如果我编辑我的答案,我不确定您是否会收到通知。我确实知道您收到了关于此的通知...
  • 感谢您的重播。 Varnish+esi 的工作方式与您的描述类似。它们现在是扩展网站以获得最佳性能的最佳方法。也许这篇文章对你来说会很有趣blog.mgm-tp.com/2012/01/varnish-web-cache
【解决方案3】:

这是一个相当棘手的问题,没有真正优雅的解决方案,如果根本没有解决方案的话

我问你它是 HTML(5) 还是 XHTML(5) 文档,因为在后一种情况下,可以使用 CDATA 部分来包装内容,稍微改变你的解决方案像这样:

...
<b id='esi-data' style='display:none;'>
    <![CDATA[ <esi:include src='apiurl/data'> ]]>
</b>
<script>
    try{
        var data = $.parseJSON($('#esi-data').html());
    }catch{ alert('manage the error here');}
....

当然,这个解决方案在以下情况下有效:

  1. 您正在使用 XHTML5 并且
  2. 错误不包含 CDATA 部分(因为 CDATA 部分嵌套是不可能的)。

我不知道从一个序列化切换到另一个序列化是否是一种选择,但我想澄清我的问题的意图。希望能帮到你:)。

【讨论】:

    【解决方案4】:

    您不能简单地更改您的 API 以在错误时返回 JSON { "error":"error_code_or_text" } 吗?如果你这样做,你甚至可以在你的界面中做一些有意义的事情来提醒用户错误。

    【讨论】:

    • 谢谢,但我无权修改 API
    【解决方案5】:
    <script>var data = 999;</script>
    
    <script>
    data = <esi:include src='apiurl/data'>;
    </script>
    
    <script>
    if(data == 999) alert("there was an error");
    </script>
    

    如果出现错误且“数据”不是 JSON,则会引发 javascript 错误。下一个脚本块将选择它。

    【讨论】:

    • 詹姆斯的问题和我之前评论过的一样。如果 是 API 错误,它将被替换为完整的 html。因此,我的页面将完全损坏。
    • 错误页面应该被隐藏,因为它在脚本标签内。当脚本标签关闭时,页面将继续。
    • 不,如果存在 /script 标签,浏览器将解析并呈现 api html。查看此示例jsfiddle.net
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-09
    • 1970-01-01
    相关资源
    最近更新 更多