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

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.xdnf.cn/news/14439.html

    相关文章:

  1. RabbitMQ消息队列实战指南
  2. 亚马逊选品时怎么选择一个产品
  3. 智能土木通 - 土木工程专业知识问答系统01:项目简介
  4. 逆元 Inverse element
  5. c语言学习_函数4
  6. 【Dify系列】【Dify 核心功能】【应用类型】【四】【Chatflow】
  7. Science 正刊:脊髓损伤患者的复杂触觉离现实又近了一步
  8. 观察者模式Observer Pattern
  9. 基于STM32的超声波模拟雷达设计
  10. 3 Studying《THE CACHE MEMORY BOOK》
  11. python3.9成功安装nbextensions
  12. 【Linux入门】安装一个Linux内核的虚拟机
  13. 【IQA技术专题】-PSNR和SSIM
  14. DOM-Based XSS(基于文档对象模型的跨站脚本攻击)
  15. leetcode 搜索插入位置 java
  16. 定时器时基单元参数配置及计算公式
  17. Python | Python中最常用的100个函数(含内置函数、标准库函数及第三方库)
  18. 基于 Transformer RoBERTa的情感分类任务实践总结之五——剪枝
  19. 使用LDA进行主题建模:发现文本中的隐藏主题 - 父亲节特别版
  20. 【旧题新解】第 9 集 带余除法
  21. router.push()
  22. 疗愈经济崛起:如何把“情绪价值”转化为医疗健康产品?
  23. 我的研究方向是关于联邦学习的数据隐私保护,这些都是我在学校过程中遇到的困惑,借助ai来解决我的问题,也分享给大家。联邦学习的公开数据集,数据集的使用方法等
  24. 《解码SCSS:悬浮与点击效果的高阶塑造法则》
  25. 电影院管理系统的设计与实现
  26. O - 方差
  27. 【项目实训】【项目博客#06】大模型微调与推理优化(4.21-5.11)
  28. Velocity提取模板变量
  29. 项目三 - 任务7:开发名片管理系统
  30. SCAU大数据技术原理期末复习|第10、11章