百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

Ajax跨域问题及解决方案 ajax跨域问题及解决方案设计

bigegpt 2024-10-12 05:57 18 浏览

目录

  • 复现Ajax跨域问题
  • Ajax跨域介绍
  • Ajax跨域解决方案
  • 一. 在服务端添加响应头Access-Control-Allow-Origin
  • 二. 使用JSONP解决
  • 小结

复现Ajax跨域问题

  • 做两个简单的小项目复现Ajax跨域问题. 后端语言使用Java
  • 首先是一个简单的订单系统, 通过访问/loadOrderList, 最终以json串形式返回订单集合. 该项目使用Tomcat发布在7070端口.
@RequestMapping("/loadOrderList")
@ResponseBody
public List<Order> loadOrderList(String uid){
 //模拟订单数据
 Order o1 = new Order();
 o1.setId("111");
 o1.setTotal(333.33);
 o1.setDate("2019-4-29");
 Order o2 = new Order();
 o2.setId("222");
 o2.setTotal(444.44);
 o2.setDate("2019-5-29");
 Order o3 = new Order();
 o3.setId("333");
 o3.setTotal(555.55);
 o3.setDate("2019-6-29");
 List<Order> list = new ArrayList<>();
 list.add(o1);
 list.add(o2);
 list.add(o3);
 return list;
}
  • 在另一个项目中做一个向订单系统发送一个ajax请求, 获取订单集合. 该项目使用Tomcat插件发布在9090端口.
//index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 <title>Title</title>
 <script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.js"></script>
 <script type="text/javascript">
 function sendAjax() {
 $.post("http://localhost:7070/order/loadOrderList", "uid=1234", function (data) {
 alert(data);
 });
 }
 </script>
</head>
<body>
 <a href="javascript:sendAjax()">sendAjax</a>
</body>
</html>

  • 点击sendAjax超链接向订单系统发送ajax请求.

  • 通过开发者工具发现虽然服务器以状态码200响应回来, 但是控制台却报错了.


  • 这就是Ajax跨域出错的一种表现, 下面分析原因.

Ajax跨域介绍

  • Ajax跨域问题是由浏览器的同源策略造成的, 首先要理解源这个概念.
  • 我们可以通过协议+域名+端口确定一个源. 在上面的示例中, 你可以把一个项目理解为一个源. Ajax请求可以对源内的资源发起访问, 但是不同源之间进行Ajax就会有问题.
  • 当向不同源的资源发起Ajax请求时, 浏览器会加上Origin字段来标识源
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Content-Length: 8
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Host: localhost:7070
Origin: http://localhost:9090 协议+域名+端口
  • 服务器会根据Origin字段决定是否同意这次请求, 如果Origin指定的源不在许可范围内, 服务器会返回一个不带有Access-Control-Allow-Origin字段的响应. 浏览器解析时发现缺少了这个字段, 就会报错. 这种错误不能通过状态码识别, 因为状态码很有可能就是200(见上面的案例).

Ajax跨域解决方案

  • 下面介绍最常用Ajax跨域解决方案.

一. 在服务端添加响应头Access-Control-Allow-Origin

  • 既然我们已经知道了Ajax跨域失败是因为响应中缺少了响应头Access-Control-Allow-Origin, 那么就想办法加上去.
  • 以Java项目为例, 在后端我们使用CORSFilter过滤器加上该响应头.
  • (假设是Maven项目), 首先在pom.xml中添加坐标
<dependency>
 <groupId>com.thetransactioncompany</groupId>
 <artifactId>cors-filter</artifactId>
 <version>2.5</version>
 <scope>runtime</scope>
</dependency>
  • 然后在web.xml中对过滤器进行配置.
 <filter>
 <filter-name>CORS</filter-name>
 <filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
 <init-param>
 <param-name>cors.allowOrigin</param-name><!--这个标签是关键, *代表所有源都能访问-->
 <param-value>*</param-value>
 </init-param>
 <init-param>
 <param-name>cors.supportedMethods</param-name>
 <param-value>GET, POST, HEAD, PUT, DELETE</param-value>
 </init-param>
 <init-param>
 <param-name>cors.supportedHeaders</param-name>
 <param-value>Accept, Origin, X-Requested-With, Content-Type, Last-Modified</param-value>
 </init-param>
 <init-param>
 <param-name>cors.exposedHeaders</param-name>
 <param-value>Set-Cookie</param-value>
 </init-param>
 <init-param>
 <param-name>cors.supportsCredentials</param-name>
 <param-value>true</param-value>
 </init-param>
 </filter>
 <filter-mapping>
 <filter-name>CORS</filter-name>
 <url-pattern>/*</url-pattern>
 </filter-mapping>

  • 配置后重启订单项目, 再次发起Ajax请求可以看到成功返回数据, 响应头中包含了Access-Control-Allow-Origin, 值为发起Ajax请求的源.


二. 使用JSONP解决

  • 上面直接通过过滤器添加响应头的方法可以说是对症下药, 那么还有没有什么偏方呢?
  • 还真的有. 在jsp文件中经常通过通过<script>标签引入一段js代码, 这段代码通常来源于网络, 也就是不同源. 那么我们不妨通过<srcipt>标签完成Ajax请求, 这样便顺带解决了跨域问题.
  • 下面还是沿用上面的案例进行演示.
  • 我们对发送ajax的jsp进行修改
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 <title>Title</title>
 <script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.js"></script>
 <script>
 function doCallBack(data){
 var str = JSON.stringify(data);
 alert(str);
 }
 </script>
</head>
<body>
 <script src="http://localhost:7070/order/loadOrderList3?uid=111&callBack=doCallBack"></script>
</body>
</html>
  • 上面的代码中, 我们首先定义了doCallBack()函数, 它接收一个字符串参数, 并且会把接收到的字符串显示出来.
  • 然后在<body>标签中编写<script>标签, 我们将通过<script>标签请求订单系统, 订单系统将会返回一段js代码, 这段js代码会调用doCallBack()方法.
  • 为了能够拼接出doCallBack(字符串参数...)js代码, 我们在订单系统中作如下操作.
@RequestMapping("/loadOrderList3")
@ResponseBody
public String loadOrderList3(String uid, String callBack){
 //模拟订单数据
 Order o1 = new Order();
 o1.setId("111");
 o1.setTotal(333.33);
 o1.setDate("2019-4-29");
 Order o2 = new Order();
 o2.setId("222");
 o2.setTotal(444.44);
 o2.setDate("2019-5-29");
 Order o3 = new Order();
 o3.setId("333");
 o3.setTotal(555.55);
 o3.setDate("2019-6-29");
 List<Order> list = new ArrayList<>();
 list.add(o1);
 list.add(o2);
 list.add(o3);
 //拼接js代码
 String result = callBack + "(" + JSON.toJSONString(list) + ")";
 return result;
}

  • 这个想法是不是很妙? 明白这个原理之后, 我们可以使用jQuery方便进行JSONP操作, 在上面的代码中我们人为指定了一个名为doCallBack的函数, 而jQuery会随机用时间戳生成一个函数名, 原理和上面是一样的.
  • 所以完成一开时点击超链接发送Ajax请求只需要如下几步.
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
 <title>Title</title>
 <script type="text/javascript" src="https://code.jquery.com/jquery-1.11.3.js"></script>
 <script>
 function sendAjax(){
 $.getJSON("http://localhost:7070/order/loadOrderList3?callBack=?","uid=111",
 function (data) {
 var str = JSON.stringify(data);
 alert(str);
 });
 }
 </script>
</head>
<body>
 <a href="javascript:sendAjax()">sendAjax</a>
</body>
</html>

小结

  • 上面两种解决办法在思路上有着本质的不同. 方案一抓住CORS跨域访问问题的本质, 在后端加上响应头解决跨域问题. 方案二JSONP利用的是<script>标签能够跨域获取js代码的特性, 绕过跨域问题.

相关推荐

得物可观测平台架构升级:基于GreptimeDB的全新监控体系实践

一、摘要在前端可观测分析场景中,需要实时观测并处理多地、多环境的运行情况,以保障Web应用和移动端的可用性与性能。传统方案往往依赖代理Agent→消息队列→流计算引擎→OLAP存储...

warm-flow新春版:网关直连和流程图重构

本期主要解决了网关直连和流程图重构,可以自此之后可支持各种复杂的网关混合、多网关直连使用。-新增Ruoyi-Vue-Plus优秀开源集成案例更新日志[feat]导入、导出和保存等新增json格式支持...

扣子空间体验报告

在数字化时代,智能工具的应用正不断拓展到我们工作和生活的各个角落。从任务规划到项目执行,再到任务管理,作者深入探讨了这款工具在不同场景下的表现和潜力。通过具体的应用实例,文章展示了扣子空间如何帮助用户...

spider-flow:开源的可视化方式定义爬虫方案

spider-flow简介spider-flow是一个爬虫平台,以可视化推拽方式定义爬取流程,无需代码即可实现一个爬虫服务。spider-flow特性支持css选择器、正则提取支持JSON/XML格式...

solon-flow 你好世界!

solon-flow是一个基础级的流处理引擎(可用于业务规则、决策处理、计算编排、流程审批等......)。提供有“开放式”驱动定制支持,像jdbc有mysql或pgsql等驱动,可...

新一代开源爬虫平台:SpiderFlow

SpiderFlow:新一代爬虫平台,以图形化方式定义爬虫流程,不写代码即可完成爬虫。-精选真开源,释放新价值。概览Spider-Flow是一个开源的、面向所有用户的Web端爬虫构建平台,它使用Ja...

通过 SQL 训练机器学习模型的引擎

关注薪资待遇的同学应该知道,机器学习相关的岗位工资普遍偏高啊。同时随着各种通用机器学习框架的出现,机器学习的门槛也在逐渐降低,训练一个简单的机器学习模型变得不那么难。但是不得不承认对于一些数据相关的工...

鼠须管输入法rime for Mac

鼠须管输入法forMac是一款十分新颖的跨平台输入法软件,全名是中州韵输入法引擎,鼠须管输入法mac版不仅仅是一个输入法,而是一个输入法算法框架。Rime的基础架构十分精良,一套算法支持了拼音、...

Go语言 1.20 版本正式发布:新版详细介绍

Go1.20简介最新的Go版本1.20在Go1.19发布六个月后发布。它的大部分更改都在工具链、运行时和库的实现中。一如既往,该版本保持了Go1的兼容性承诺。我们期望几乎所...

iOS 10平台SpriteKit新特性之Tile Maps(上)

简介苹果公司在WWDC2016大会上向人们展示了一大批新的好东西。其中之一就是SpriteKitTileEditor。这款工具易于上手,而且看起来速度特别快。在本教程中,你将了解关于TileE...

程序员简历例句—范例Java、Python、C++模板

个人简介通用简介:有良好的代码风格,通过添加注释提高代码可读性,注重代码质量,研读过XXX,XXX等多个开源项目源码从而学习增强代码的健壮性与扩展性。具备良好的代码编程习惯及文档编写能力,参与多个高...

Telerik UI for iOS Q3 2015正式发布

近日,TelerikUIforiOS正式发布了Q32015。新版本新增对XCode7、Swift2.0和iOS9的支持,同时还新增了对数轴、不连续的日期时间轴等;改进TKDataPoin...

ios使用ijkplayer+nginx进行视频直播

上两节,我们讲到使用nginx和ngixn的rtmp模块搭建直播的服务器,接着我们讲解了在Android使用ijkplayer来作为我们的视频直播播放器,整个过程中,需要注意的就是ijlplayer编...

IOS技术分享|iOS快速生成开发文档(一)

前言对于开发人员而言,文档的作用不言而喻。文档不仅可以提高软件开发效率,还能便于以后的软件开发、使用和维护。本文主要讲述Objective-C快速生成开发文档工具appledoc。简介apple...

macOS下配置VS Code C++开发环境

本文介绍在苹果macOS操作系统下,配置VisualStudioCode的C/C++开发环境的过程,本环境使用Clang/LLVM编译器和调试器。一、前置条件本文默认前置条件是,您的开发设备已...