当前位置: 首页 > news >正文

JSONP 跨域请求原理解析与实践

JSONP(JSON with Padding)是一种跨域数据交互技术,虽然它不是真正的 AJAX 请求,但可以实现类似的局部刷新效果。本文将深入解析 JSONP 的原理,并通过完整的 Java 后端和 JavaScript 前端示例演示其工作流程。

一、JSONP 的基本原理

传统的 AJAX 请求受同源策略限制,而 JSONP 利用了<script>标签不受同源策略约束的特性。其核心流程如下:

  1. 前端动态创建一个<script>标签,src 指向跨域的 API 接口,并添加一个回调函数名作为参数(例如:callback=handleResponse
  2. 服务器收到请求后,将 JSON 数据包装在回调函数中返回(例如:handleResponse({"name":"John","age":30})
  3. 当 script 标签加载完成后,会执行这个回调函数,从而获取到服务器返回的数据

二、完整代码实现

下面是一个完整的 JSONP 示例,包含 Java 后端和 JavaScript 前端代码:

jsonp-client.html

<!DOCTYPE html>
<html>
<head><title>JSONP跨域请求示例</title>
</head>
<body><h3>JSONP跨域请求演示</h3><button onclick="fetchData()">获取跨域数据</button><div id="result"></div><script>function fetchData() {// 生成唯一的回调函数名,避免命名冲突const callbackName = 'jsonpCallback_' + Date.now();// 创建script标签const script = document.createElement('script');// 定义回调函数window[callbackName] = function(data) {// 处理返回的数据document.getElementById('result').innerHTML = `姓名: ${data.name}, 年龄: ${data.age}, 城市: ${data.city}`;// 处理完成后移除script标签和回调函数document.body.removeChild(script);delete window[callbackName];};// 设置script的src,指向跨域API并带上回调函数名script.src = 'http://localhost:8080/jsonp?callback=' + callbackName;// 错误处理script.onerror = function() {document.getElementById('result').innerHTML = '请求失败,请检查服务器';document.body.removeChild(script);delete window[callbackName];};// 将script添加到页面中,触发请求document.body.appendChild(script);}</script>
</body>
</html>    

JsonpServer.java

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;@WebServlet("/jsonp")
public class JsonpServer extends HttpServlet {protected void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {// 设置响应头response.setContentType("application/javascript");response.setCharacterEncoding("UTF-8");// 获取回调函数名String callback = request.getParameter("callback");if (callback == null || callback.isEmpty()) {response.getWriter().write("Invalid JSONP request");return;}// 模拟业务数据String jsonData = "{\"name\":\"John\",\"age\":30,\"city\":\"New York\"}";// 构造JSONP响应:callbackFunction(jsonData)String jsonpResponse = callback + "(" + jsonData + ")";// 返回JSONP响应response.getWriter().write(jsonpResponse);}
}    

三、代码解析

  1. Java 后端

    • 创建了一个 Servlet 处理 JSONP 请求
    • 从请求参数中获取回调函数名
    • 将 JSON 数据包装在回调函数中返回
    • 设置正确的 Content-Type 为application/javascript
  2. 前端实现

    • 动态创建 script 标签并指定跨域 URL
    • 定义全局回调函数处理返回的数据
    • 请求完成后清理 DOM 和全局变量
    • 添加了错误处理机制

四、JSONP 的优缺点

优点

  • 兼容性好,支持所有主流浏览器
  • 实现简单,不需要额外的服务器配置
  • 可以绕过同源策略限制

缺点

  • 只支持 GET 请求
  • 存在安全风险(JSONP 注入攻击)
  • 错误处理相对复杂
  • 只适用于获取 JSON 数据

五、使用注意事项

  1. 确保对回调函数名进行严格的输入验证,防止 XSS 攻击
  2. 生产环境中应使用 HTTPS 协议
  3. 考虑使用 CORS 作为更现代的跨域解决方案,除非需要兼容非常旧的浏览器
  4. 对返回的 JSON 数据进行适当的安全过滤

JSONP 虽然不是一个真正的 AJAX 请求,但在特定场景下(如需要兼容旧浏览器),它仍然是一个有效的跨域解决方案。通过理解其原理和实现方式,开发者可以在合适的场景下选择最佳的跨域策略。

六、实现类似的局部刷新效果怎么实现的?

AJAX 请求依赖浏览器的 XMLHttpRequest 或 Fetch API,受同源策略限制;而 JSONP 借助 <script> 标签的跨域特性实现数据获取,虽非真正的 AJAX,却能达成局部刷新效果。以下是其核心实现逻辑与示例:

1.JSONP 实现局部刷新的核心原理

  1. 利用 <script> 标签的跨域特性

    • 浏览器中,<script> 标签的 src 属性可请求任意域名的资源(如 JavaScript 文件),不受同源策略限制。
    • JSONP 本质是动态加载一个包含数据的 JavaScript 脚本,通过执行脚本中的函数来获取数据。
  2. 局部刷新的实现逻辑

    • 前端动态创建 <script> 标签并发起请求,服务器返回的脚本包含回调函数调用(如 callback({data}))。
    • 脚本加载完成后自动执行回调函数,将数据渲染到页面指定区域,无需刷新整个页面,从而实现 “局部刷新”。

2.代码示例:JSONP 实现局部刷新

前端页面(HTML + JavaScript)

<!DOCTYPE html>
<html>
<head><title>JSONP 局部刷新示例</title>
</head>
<body><h3>JSONP 局部刷新演示</h3><button onclick="loadData()">点击获取数据</button><div id="content">数据将显示在这里</div><script>function loadData() {// 生成唯一回调函数名,避免冲突const callbackName = `jsonp_${Date.now()}`;// 定义回调函数(挂载到 window 全局对象)window[callbackName] = function(response) {// 将数据渲染到页面,实现局部刷新document.getElementById('content').innerHTML = `<p>姓名:${response.name}</p><p>年龄:${response.age}</p><p>内容:${response.content}</p>`;// 清理回调函数和 script 标签document.body.removeChild(scriptElement);delete window[callbackName];};// 创建 script 标签并设置 srcconst scriptElement = document.createElement('script');scriptElement.src = `http://localhost:8080/jsonp?callback=${callbackName}&param=1`;document.body.appendChild(scriptElement);}</script>
</body>
</html>

Java 后端(Spring Boot 示例)

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class JsonpController {@GetMapping("/jsonp")public String jsonp(@RequestParam("callback") String callback) {// 构造数据String data = "{\"name\":\"JSONP示例\",\"age\":1,\"content\":\"通过脚本加载实现局部刷新\"}";// 将数据包装在回调函数中返回(JSONP 核心)return callback + "(" + data + ")";}
}

3.局部刷新的实现关键点

  1. 动态渲染数据
    回调函数接收到数据后,通过 DOM 操作(如 document.getElementById('content').innerHTML)直接更新页面指定区域,无需刷新整个页面。

  2. 脚本加载与执行流程

    • 前端创建 <script> 标签并插入页面 → 浏览器发送 GET 请求到服务器。
    • 服务器返回 callback({data}) 格式的脚本 → 浏览器加载并执行脚本,触发回调函数。
    • 回调函数更新页面 DOM,完成局部刷新。
  3. 与 AJAX 的区别

    特性AJAX(XHR/Fetch)JSONP
    请求方式依赖 XMLHttpRequest依赖 <script> 标签
    跨域支持需服务器配置 CORS天然支持跨域
    请求类型支持 GET/POST 等仅支持 GET
    局部刷新实现通过 XHR 获取数据后渲染通过脚本执行回调函数渲染

七、<script> 标签的 src 属性确实会触发资源加载,但不会导致页面跳转

<script> 标签的 src 属性确实会触发资源加载,但不会导致页面跳转。这是因为浏览器对不同类型的资源有不同的处理方式:

1.核心原理:<script> 加载不会跳转页面

当你使用 <script src="..."> 时,浏览器会:

  1. 异步加载指定的 JavaScript 文件(默认不阻塞页面渲染,除非设置了 defer 或 async)。
  2. 执行脚本内容,但不会导航到新页面
  3. 脚本执行过程中,可通过 DOM 操作更新页面的局部区域,从而实现 “局部刷新”。

2.对比理解:不同标签的行为差异

标签类型示例代码浏览器行为
<a> 链接<a href="http://example.com">点击后跳转至新 URL,整个页面刷新。
<img> 图片<img src="image.jpg">加载图片资源并显示在页面指定位置,不影响其他内容,不跳转页面。
<script> 脚本<script src="script.js">加载 JavaScript 代码并执行,允许通过脚本修改页面(如更新某个 <div> 的内容),不跳转页面。

3.JSONP 如何利用这一特性实现局部刷新?

示例代码(回顾)

<script>function loadData() {const callbackName = `jsonp_${Date.now()}`;window[callbackName] = function(data) {// 更新页面局部区域(如 ID 为 content 的 div)document.getElementById('content').innerHTML = `姓名:${data.name}`;};// 创建 script 标签const script = document.createElement('script');script.src = `http://api.example.com/data?callback=${callbackName}`;document.body.appendChild(script);}
</script>

4.执行流程详解

动态创建 <script> 标签

const script = document.createElement('script');
script.src = 'http://api.example.com/data?callback=jsonp_12345';
document.body.appendChild(script);

    浏览器发送 GET 请求到 http://api.example.com/data?callback=jsonp_12345

    服务器返回 JSONP 格式的脚本
    服务器收到请求后,返回如下内容(注意这是一个 JavaScript 脚本):

    jsonp_12345({"name": "John","age": 30
    });

    浏览器执行脚本

    脚本加载完成后,浏览器执行 jsonp_12345(...) 函数。

    由于这个函数已在前端定义(window[callbackName] = ...),数据会被传递到回调函数中。

    回调函数通过 document.getElementById('content').innerHTML 更新页面局部内容。

    最终效果

    页面没有跳转,只有 #content 区域的内容被更新,实现了 “局部刷新”。

    5.关键区别:导航 vs. 资源加载

    1. 导航(Navigation)

      • 当用户点击链接(<a>)、在地址栏输入 URL、调用 window.location.href = '...' 时,浏览器会加载新页面,丢弃当前页面状态。
    2. 资源加载(Resource Loading)

      • 当使用 <script><img><link> 等标签加载资源时,浏览器仅获取指定资源并集成到当前页面中,不会跳转

    http://www.lryc.cn/news/570793.html

    相关文章:

  1. 索尼手机c6802的Android,索尼C6802 (Xperia UL)图文刷机教程,最新教程,简单明了
  2. 帝国导航高亮显示(只支持一级栏目,灵动标签)
  3. [AHK]用 AutoHotkey 做 web 服务器~ (例如远程执行 AutoHotkey 脚本...)
  4. 云计算40个问题
  5. 甲骨文和Sun的结合:开源成或败?
  6. iOS —— UI(2)
  7. STM32——UCOSIII挂起恢复与时间轮转
  8. log4j2远程代码执行漏洞复现
  9. 手机qq java版 2008,手机qq2008
  10. 由一张图开始
  11. [附源码]java毕业设计大学城二手书交易网站
  12. 详解jar包的启动命令
  13. Java中的CAS与ABA
  14. 20年架构师视角:SpringAI如何重塑Java技术栈?
  15. 【矩阵运算c++实现】矩阵封装实现Matrix类
  16. 基于SVD的推荐系统:详尽的原理与实践解析
  17. Google colabo使用说明
  18. JAVA高校校园学校网站系统设计与实现(Springboot框架)
  19. JavaSE: 数组详解
  20. 按键精灵 获取网页flash游戏 句柄 以360浏览器为例
  21. QQ空间 免费代码大全
  22. 启动应用程序出现SensApi.dll找不到问题解决
  23. 【八股消消乐】构建微服务架构体系—链路超时控制
  24. 给大家推荐一些好用的国内外免费图床(可外链的免费相册)
  25. 用SVN下载sourceforge上的源代码
  26. va_list、va_start和va_end的用法
  27. LAN_IM: 在局域网内实现高效即时通讯的开源解决方案
  28. 计算机在线给手机杀毒,怎么用电脑给手机杀毒(如果手机中病毒了怎么办)
  29. 如何写一个简单的python类class
  30. STM32学习笔记:读写内部Flash(介绍+附代码)