# WebApi 跨域问题解决方案:CORS

作者:网络 时间: 2020-10-16

# 一、跨域问题的由来

同源策略:出于安全考虑,浏览器会限制脚本中发起的跨站请求,浏览器要求 JavaScript 或 Cookie 只能访问同域下的内容。

正是由于这个原因,我们不同项目之间的调用就会被浏览器阻止。比如我们最常见的场景:WebApi 作为数据服务层,它是一个单独的项目,我们的 MVC 项目作为 Web 的显示层,这个时候我们的 MVC 里面就需要调用 WebApi 里面的接口取数据展现在页面上。因为我们的 WebApi 和 MVC 是两个不同的项目,所以运行起来之后就存在上面说的跨域的问题。

回到顶部

# 二、跨域问题解决原理

CORS 全称 Cross-Origin Resource Sharing,中文全称跨域资源共享。它解决跨域问题的原理是通过向 http 的请求报文和响应报文里面加入相应的标识告诉浏览器它能访问哪些域名的请求。比如我们向响应报文里面增加这个 Access-Control-Allow-Origin:http://localhost:8081,就表示支持http://localhost:8081里面的所有请求访问系统资源。其他更多的应用我们就不一一列举,可以去网上找找。

回到顶部

# 三、跨域问题解决细节

下面我就结合一个简单的实例来说明下如何使用 CORS 解决 WebApi 的跨域问题。

回到顶部

# 1、场景描述

我们新建两个项目,一个 WebApi 项目(下图中 WebApiCORS),一个 MVC 项目(下图中 Web)。WebApi 项目负责提供接口服务,MVC 项目负责页面呈现。如下:

img

其中,**Web 与**WebApiCORS 端口号分别为“**27239”和“27221”。**Web 项目需要从 WebApiCORSS 项目里面取数据,很显然,两个项目端口不同,所以并不同源,如果使用常规的调用方法肯定存在一个跨域的问题。

简单介绍下测试代码,Web 里面有一个 HomeController

   public class HomeController : Controller
    {
        // GET: Home
        public ActionResult Index()
        {
            return View();
        }
    }
1
2
3
4
5
6
7
8

对应的 Index.cshtml

<html>
  <head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
    <script src="~/Content/jquery-1.9.1.js"></script>
    <link href="~/Content/bootstrap/css/bootstrap.css" rel="stylesheet" />
    <script src="~/Content/bootstrap/js/bootstrap.js"></script>
    <script src="~/Scripts/Home/Index.js"></script>
  </head>
  <body>
    测试结果:
    <div id="div_test"></div>
  </body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Index.js 文件

var ApiUrl = "http://localhost:27221/";
$(function () {
  $.ajax({
    type: "get",
    url: ApiUrl + "api/Charging/GetAllChargingData",
    data: {},
    success: function (data, status) {
      if (status == "success") {
        $("#div_test").html(data);
      }
    },
    error: function (e) {
      $("#div_test").html("Error");
    },
    complete: function () {},
  });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

WebApiCORS 项目里面有一个测试的 WebApi 服务 ChargingController

  public class ChargingController : ApiController
    {
        /// <summary>
        /// 得到所有数据
        /// </summary>
        /// <returns>返回数据</returns>
        [HttpGet]
        public string GetAllChargingData()
        {
            return "Success";
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12

配置 WebApi 的路由规则为通过 action 调用。WebApiConfig.cs 文件

   public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API 路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2、场景测试

# 1)我们不做任何的处理,直接将两个项目运行起来。看效果如何

IE 浏览器:

img

谷歌浏览器:

img

这个结果另博主也很吃惊,不做任何跨域处理,IE10、IE11 竟然可以直接请求数据成功,而同样的代码 IE8、IE9、谷歌浏览器却不能跨域访问。此原因有待查找,应该是微软动了什么手脚。

# 2)使用 CORS 跨域

首先介绍下 CORS 如何使用,在 WebApiCORS 项目上面使用 Nuget 搜索“microsoft.aspnet.webapi.cors”,安装第一个

img

然后在 App_Start 文件夹下面的 WebApiConfig.cs 文件夹配置跨域

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            //跨域配置
            config.EnableCors(new EnableCorsAttribute("*", "*", "*"));

            // Web API 路由
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

我们暂定三个“*”号,当然,在项目中使用的时候一般需要指定对哪个域名可以跨域、跨域的操作有哪些等等。这个在下面介绍。

IE10、IE11

img

谷歌浏览器

img

IE8、IE9

img

这个时候又有新问题了,怎么回事呢?我都已经设置跨域了呀,怎么 IE8、9 还是不行呢?这个时候就有必要说说 CORS 的浏览器支持问题了。网上到处都能搜到这张图:

img

上图描述了 CORS 的浏览器支持情况,可以看到 IE8、9 是部分支持的。网上说的解决方案都是 Internet Explorer 8 、9 使用 XDomainRequest 对象实现 CORS。是不是有这么复杂?于是博主各种百度寻找解决方案。最后发现在调用处指定 jQuery.support.cors = true; 这一句就能解决 IE8、9 的问题了。具体是在 Index.js 里面

jQuery.support.cors = true;
var ApiUrl = "http://localhost:27221/";
$(function () {
    $.ajax({
        type: "get",
        url: ApiUrl + "api/Charging/GetAllChargingData",
        data: {},
        success: function (data, status) {
            if (status == "success") {
                $("#div_test").html(data);
            }
        },
        error: function (e) {
            $("#div_test").html("Error");
        },
        complete: function () {

        }
    });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

这句话的意思就是指定浏览器支持跨域。原来 IE9 以上版本的浏览器、谷歌、火狐等都默认支持跨域,而 IE8、9 却默认不支持跨域,需要我们指定一下。你可以在你的浏览器里面打印 jQuery.support.cors 看看。这样设置之后是否能解决问题呢?我们来看效果:

img

问题完美解决。至于网上说的 CORS 对 IE8、9 的解决方案 XDomainRequest 是怎么回事,有待实例验证。

# 3)CORS 的具体参数设置。

上文我们使用

config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
1

这一句解决了跨域问题,上面说了,这种*号是不安全的。因为它表示只要别人知道了你的请求 url,任何请求都可以访问到你的资源。这是相当危险的。所以需要我们做一些配置,限制访问权限。比如我们比较常见的做法如下:

配置方法一、在 Web.Config 里面(PS:这两张图源自:http://www.cnblogs.com/moretry/p/4154479.html)

img

然后在 WebApiConfig.cs 文件的 Register 方法里面

img

配置方法二、如果你只想对某一些 api 做跨域,可以直接在 API 的类上面使用特性标注即可。

  [EnableCors(origins: "http://localhost:8081/", headers: "*", methods: "GET,POST,PUT,DELETE")]
    public class ChargingController : ApiController
    {
        /// <summary>
        /// 得到所有数据
        /// </summary>
        /// <returns>返回数据</returns>
        [HttpGet]
        public string GetAllChargingData()
        {
            return "Success";
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13

# 四、总结

以上就是一个简单的 CORS 解决 WebApi 跨域问题的实例,由于博主使用 WebApi 的时间并不长,所以很多理论观点未必成熟,如果有说的不对的,欢迎指出。博主在此多谢啦。