JSONP:过去前端开发者的秘密武器
\n想象一下,你是2010年的一名Web开发者。你需要从别人的服务器获取数据,比如天气预报或汇率。你写了一个简单的fetch请求,但浏览器控制台却出现了一个红色错误:「No 'Access-Control-Allow-Origin' header is present」。熟悉吗?
这是同源策略(Same-Origin Policy)阻止了你的JavaScript访问本域名之外的内容。但天气插件、分享按钮和访问计数器又是如何工作的呢?答案很简单:它们使用了JSONP——一个我们今天要讨论的巧妙技巧。
\n在本文中,我们将用通俗易懂的语言,配合代码示例,回答Stack Overflow上一个热门问题:「What is JSONP, and why was it created?」,不涉及多余的理论。
\n\n1. 问题:为什么浏览器会阻止请求?
\n浏览器是个偏执狂。它保护用户免受XSS攻击和数据窃取。同源策略只允许JavaScript读取来自相同域名、协议和端口的响应。
\n阻止示例:
\n// 这段代码会引发CORS错误\nfetch('https://api.distant-server.com/data')\n .then(response => response.json())\n .then(data => console.log(data));\n但有一个例外:<script>标签。浏览器可以安全地从任何域名加载脚本!JSONP正是利用了这一点。
\n\n2. 解决方案:JSONP——带填充的JSON
\nJSONP(JSON with Padding)不是一种技术或数据格式,而是一种模式(权宜之计),它通过<script>标签绕过同源策略。
思路很简单:我们不通过XMLHttpRequest发起请求,而是动态创建一个<script>元素,指定服务器URL,然后服务器返回的不是JSON,而是JavaScript代码,该代码调用我们的函数并传入数据。
普通JSON的样子:
\n// 服务器响应(普通JSON)\n{"name": "PythonLib", "topic": "JSONP"}\n\nJSONP的样子:
\n// 服务器响应(JSONP)\nmyCallback({"name": "PythonLib", "topic": "JSONP"});\n区别巨大:在第二种情况下,服务器将数据包装在函数调用myCallback中。这就是所谓的padding(填充)。
3. 实践:自己动手写一个JSONP请求
\n让我们用纯JavaScript实现一个JSONP客户端。这有助于理解其中的魔法。
\n\nfunction jsonp(url, callbackName) {\n // 1. 创建一个全局函数(回调)\n window[callbackName] = function(data) {\n console.log('数据已获取:', data);\n delete window[callbackName]; // 清理\n document.body.removeChild(script);\n };\n\n // 2. 创建一个script标签\n const script = document.createElement('script');\n script.src = `${url}?callback=${callbackName}`; // 传递回调名称\n script.onerror = function() {\n console.error('JSONP加载错误');\n delete window[callbackName];\n document.body.removeChild(script);\n };\n\n // 3. 添加到DOM——浏览器立即加载并执行脚本\n document.body.appendChild(script);\n}\n\n// 使用:\njsonp('https://api.example.com/data', 'handleData');\n逐步工作原理:
\n- \n
- 我们创建一个全局函数
handleData。 \n - 添加一个
<script>标签,指向服务器URL。在URL中,我们传递参数?callback=handleData。 \n - 支持JSONP的服务器看到
callback参数后,返回的不是JSON,而是JavaScript:handleData({...})。 \n - 浏览器执行这个脚本,我们的函数被调用,从而获取到数据。 \n
4. 为什么JSONP是邪恶的?(缺点)
\n如果你在想:「太棒了,我要到处使用它」,那么请三思。JSONP有几个严重的缺点:
\n- \n
- 安全风险:你完全信任第三方服务器。如果它被攻破,恶意脚本可以访问你的全局作用域。 \n
- 仅支持GET请求:JSONP只能通过
<script>标签工作,而标签只能发送GET请求。无法发送POST、PUT或DELETE。 \n - 错误处理困难:如果服务器返回错误或宕机,你无法轻松捕获错误(尽管
onerror有所帮助)。 \n - 全局命名空间污染:每个JSONP请求都会在全局
window对象上创建一个函数。如果不清理,会导致冲突。 \n
5. 救世主:CORS如何终结了JSONP时代
\n2014年,CORS(跨域资源共享)成为标准。这是一种更安全、更灵活的跨域请求方式。
\nCORS允许服务器通过HTTP头显式指定哪些来源可以访问其资源:
\n// 服务器响应头\nAccess-Control-Allow-Origin: https://my-site.com\nAccess-Control-Allow-Methods: GET, POST, PUT\nAccess-Control-Allow-Headers: Content-Type\n现在,你只需使用普通的fetch或XMLHttpRequest,服务器通过头信息决定是否允许请求。不需要回调、全局函数或<script>标签。
6. 结论:JSONP已死,CORS万岁
\nJSONP是一个巧妙的技巧,解决了特定时代的问题。但如今,它已被视为过时的技术。现代浏览器和API都使用CORS。
\n何时仍可能遇到JSONP?
\n- \n
- 旧版API(例如,一些2010年代的天气服务)。 \n
- 不支持CORS的遗留系统。 \n
- 面试题(是的,他们仍然会问这个)。 \n
但如果你在2024年启动一个新项目,请忘记JSONP。使用CORS,它更安全、更简单、更强大。
\n\n7. 总结
\n我们讨论了:
\n- \n
- 同源策略及其限制。 \n
- JSONP如何通过
<script>标签绕过这些限制。 \n - JSONP的缺点(安全性、仅支持GET、错误处理)。 \n
- CORS如何成为现代标准。 \n
现在你知道了JSONP是什么,以及为什么它被创建。这是一个很好的历史教训,展示了开发者如何巧妙地解决浏览器限制。但请记住:对于新项目,使用CORS。