JavaScript 跨域请求(CORS)

小占时光 2025-04-11 11:54:59 24


背景

最近来个一个新同事,在开发功能时遇到sj跨域请求的问题,他调整了一天都没有搞好,看来有必要记录一下。在前端开发中,跨域请求(Cross-Origin Request) 是一个非常常见,但又让人头疼的问题。很多初学者在使用 AJAX 调用后端接口时,经常会遇到。跨域是一个比较老的问题了,解决方案也很多。对于.net 开发来说,一般都是修改webconfig。

跨域问题错误的表现

我们在前端页面访问一个后端的服务的api接口(/api/snappic/matting/ping接口正常应该返回字符串“Pong”),但请求没有获取到数据错误如下图。

但我们直接使用浏览器访问这个接口是可以正常返回数据的,证明这个api接口是正常的,没有问题。上面的错误就是跨域了。

一、什么是“跨域”?

在浏览器中,只要你通过 JavaScript 发起 HTTP 请求,如果请求的 协议、域名、端口 任何一个与当前页面的地址不同,就会被浏览器认为是“跨域”。通俗一点将就是用一个网站去访问另一个网站。

例如:上面的例子,网站的地址是:http://localhost:8066/ ,但去请求http://192.168.100.40:8033,这两个不是同一个网站。

二、为什么浏览器要限制跨域?

这是出于 浏览器的安全机制 —— 同源策略(Same-Origin Policy)

同源策略是浏览器的一项重要安全策略,它规定了不同源之间的脚本是无法相互访问数据的,以防止恶意网站窃取用户数据。

三、CORS 是什么?

CORS,全称 Cross-Origin Resource Sharing(跨源资源共享),是 W3C 标准,允许浏览器向跨源服务器发出 XMLHttpRequest 或 Fetch 请求的一种机制。

它不是浏览器能“直接打开”的功能,而是需要 服务器端返回正确的 HTTP 响应头 来授权浏览器访问。

四、CORS 的工作原理

当浏览器发出一个跨域请求时,会按照以下流程工作:

1. 简单请求

比如使用 GET 或 POST,但不携带特殊头部时,浏览器会直接发起请求。

服务器若允许该请求,需返回如下 HTTP 响应头:

Access-Control-Allow-Origin: https://www.example.com

说明:如果服务端设置为 *,表示允许所有来源访问。

2.复杂请求(Preflight 预检请求)

如果你使用了以下任意一种情况:

  • 请求方法是 PUT、DELETE、PATCH
  • 自定义头部(如 Authorization)
  • Content-Type 是 application/json

浏览器会 先发送一个 OPTIONS 请求,称为“预检请求(Preflight Request)”。

服务器必须返回如下头部,浏览器才会继续发起实际请求:

Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization

五、前端如何发起 CORS 请求?

这里首先明确一个问题,前端不能并不能设置让某一个指定API接收跨域请求,跨域是需要配置服务端配置的。下面就是.net web项目来做例子。

后端api接口代码(APP1 应用)

 [Route("api/snappic/matting")]
 [ApiController]
 public class MattingController : ControllerBase
 {
     [HttpGet]
     [Route("ping")]
     public async Task<string> Ping()
     {
         return "Pong";
     }
}

前端js请求代码(APP2 应用)

$.ajax({
     url: ‘http://192.168.100.40:8033/api/snappic/matting/ping’, // 请求 URL
     type: 'GET', // 请求方式 GET 或 POST
     success: function (response) {
          console.log('成功响应数据:', response);
          // 你可以在这里处理返回的数据
      }
});

使用默认配置,我将两个应用都发布到iis,APP1 应用的地址是http://192.168.100.40:8033/api/snappic/matting/ping ,APP2 应用地址http://localhost:8066。运行起来结果就如开始的截图所示。

可以看到虽然返回状态是200,但是请求已经被拦截,获取不到任何数据。

新手在这个时候可能会不停找APP2 应用的问题,但是问题是APP1 禁止了APP2 的请求,如果要正常访问就需要指定APP1 允许APP2访问。配置方法的话,只需要在APP2 的webconfig中添加允许跨域的配置即可。不管什么语言开发的web应用都是同一原理。

后端配置

只需webconfig修改成一下配置即可。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <!-- 配置当前站点 -->
  <location path="." inheritInChildApplications="false">
    <system.webServer>
      <!-- ASP.NET Core 配置 -->
      <handlers>
        <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
        <!-- 允许处理 OPTIONS 请求(预检请求) -->
        <add name="OPTIONS" path="*" verb="OPTIONS" type="System.Web.HttpNotFoundHandler" resourceType="Unspecified" />
      </handlers>
      <aspNetCore processPath="dotnet" arguments=".\Snappic.dll" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" hostingModel="inprocess" />
      
      <!-- CORS 配置 -->
      <httpProtocol>
        <customHeaders>
          <!-- 允许所有域进行跨域请求 -->
          <add name="Access-Control-Allow-Origin" value="*" />
          
          <!-- 允许的 HTTP 请求方法 -->
          <add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS" />
          
          <!-- 允许的请求头 -->
          <add name="Access-Control-Allow-Headers" value="Content-Type, Authorization, X-Requested-With" />
          
          <!-- 如果需要携带认证信息(如 Cookies),则添加此项 -->
          <add name="Access-Control-Allow-Credentials" value="false" />
        </customHeaders>
      </httpProtocol>
    </system.webServer>
  </location>
</configuration>

这里有一个点需要注意:

Access-Control-Allow-Credentials 设置为false表示不带身份信息,Access-Control-Allow-Origin 可以设置成 * ,表示允许所有域进行跨域请求。

Access-Control-Allow-Credentials 设置为true表示带身份信息,Access-Control-Allow-Origin 必须写出指定的网址地址(域名或者IP) ,表示允许指定的网站访问。同时前端需要带上 withCredentials,反过来也是一样,如果ajax请求带上了withCredentials,则webconfig中必须Access-Control-Allow-Credentials 设置为true,否则就会报错。

$.ajax({
  url: "https://api.example.com/data",
  method: "GET",
  xhrFields: {
    withCredentials: true // 启用 cookie 发送
  },
  success: function (data) {
    console.log("数据:", data);
  },
  error: function (xhr, status, err) {
    console.error("错误:", err);
  }
});

做好配置,我们在发布一个APP3 应用地址是http://192.168.100.40:8044,Api代码不做任何改变。我测试一下两个请求。

APP3 应用已经能正常访问,响应头部也添加了允许跨域的信息。(.net除了webconfig也可以在后端代码中做相同配置,可去网上搜索一下)

好了就这些,加油!

 

最后一次修改 : 2025/4/16 下午9:25:35

优秀
123
分享
123
奶茶
123
文章版权声明:版权归原作者(小占时光)所有。未经明确书面许可,严禁任何个人或组织以任何形式进行商业性或非商业性的使用、转载或抄袭。
评论