WebAPI Ajax 跨域请求解决方法(CORS实现)

概述

 

ASP.NET Web API 的好用使用过的都知道,没有复杂的配置文件,一个简单的ApiController加上需要的Action就能工作。

 

但是在使用API的时候总会遇到跨域请求的问题,特别各种APP万花齐放的今天,API的跨域请求是不能避免的。

 

在默认情况下,为了防止CSRF跨站的伪造攻击(或者是 javascript的同源策略(Same-Origin Policy)),一个网页从另外一个域获取数据时就会收到限制。

 

有一些方法可以突破这个限制,那就是大家熟知的JSONP, 当然这只是众多解决方法中一种,由于JSONP只支持GET的请求,如今的复杂业务中已经不能满足需求。

 

而CORS(Cross Origin Resource Sharing https://www.w3.org/wiki/CORS)跨域资源共享,是一种新的header规范,可以让服务器端放松跨域的限制,可以根据header来切换限制或者不限制跨域请求。重要的是它支持所有http请求方式。

 

问题

 

XMLHttpRequest 跨域 POST或GET请求 ,请求方式会自动变成OPTIONS的问题。

 

由于CORS(cross origin resource share)规范的存在,浏览器会首先发送一次options嗅探,同时header带上origin,判断是否有跨域请求权限,服务器响应access control allow origin的值,供浏览器与origin匹配,如果匹配则正式发送post请求,即便是服务器允许程序跨域访问,若不支持 options 请求,请求也会死掉。

 

原因

 

浏览器为了安全起见,会Preflighted Request的透明服务器验证机制支持开发人员使用自定义的头部、GET或POST之外的方法,以及不同类型的主题内容,也就是会先发送一个 options 请求,问问服务器是否会正确(允许)请求,确保请求发送是安全的。

 

出现 OPTIONS 的情况一般为:

 

1、非GET 、POST请求

 

2、POST请求的content-type不是常规的三个:application/x- www-form-urlencoded(使用 HTTP 的 POST 方法提交的表单)、multipart/form-data(同上,但主要用于表单提交时伴随文件上传的场合)、text/plain(纯文本) 

 

3、POST请求的payload为text/html 

 

4、设置自定义头部

 

OPTIONS请求头部中会包含以下头部:Origin、Access-Control-Request-Method、Access-Control-Request-Headers,发送这个请求后,服务器可以设置如下头部与浏览器沟通来判断是否允许这个请求。

Access-Control-Allow-Origin、Access-Control-Allow-Method、Access-Control-Allow-Headers

 

解决方法

 

此方法功能强大,可以解决ASP.NET Web API复杂跨域请求,携带复杂头部信息,正文内容和授权验证信息

 

方法一

  1 public class CrosHandler:DelegatingHandler
  2 
  3 {
  4 
  5     private const string Origin = "Origin";
  6 
  7     private const string AccessControlRequestMethod = "Access-Control-Request-Method";
  8 
  9     private const string AccessControlRequestHeaders = "Access-Control-Request-Headers";
 10 
 11     private const string AccessControlAllowOrign = "Access-Control-Allow-Origin";
 12 
 13     private const string AccessControlAllowMethods = "Access-Control-Allow-Methods";
 14 
 15     private const string AccessControlAllowHeaders = "Access-Control-Allow-Headers";
 16 
 17     private const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials";
 18 
 19     protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
 20 
 21     {
 22 
 23         bool isCrosRequest = request.Headers.Contains(Origin);
 24 
 25         bool isPrefilightRequest = request.Method == HttpMethod.Options;
 26 
 27         if (isCrosRequest)
 28 
 29         {
 30 
 31             Task<HttpResponseMessage> taskResult = null;
 32 
 33             if (isPrefilightRequest)
 34 
 35             {
 36 
 37                 taskResult = Task.Factory.StartNew<HttpResponseMessage>(() =>
 38 
 39                 {
 40 
 41                     HttpResponseMessage response = new HttpResponseMessage(System.Net.HttpStatusCode.OK);
 42 
 43                     response.Headers.Add(AccessControlAllowOrign,
 44 
 45                         request.Headers.GetValues(Origin).FirstOrDefault());
 46 
 47                     string method = request.Headers.GetValues(AccessControlRequestMethod).FirstOrDefault();
 48 
 49                     if (method != null)
 50 
 51                     {
 52 
 53                         response.Headers.Add(AccessControlAllowMethods, method);
 54 
 55                     }
 56 
 57                     string headers = string.Join(", ", request.Headers.GetValues(AccessControlRequestHeaders));
 58 
 59                     if (!string.IsNullOrWhiteSpace(headers))
 60 
 61                     {
 62 
 63                         response.Headers.Add(AccessControlAllowHeaders, headers);
 64 
 65                     }
 66 
 67                     response.Headers.Add(AccessControlAllowCredentials, "true");
 68 
 69                     return response;
 70 
 71                 }, cancellationToken);
 72 
 73             }
 74 
 75             else
 76 
 77             {
 78 
 79                 taskResult = base.SendAsync(request, cancellationToken).ContinueWith<HttpResponseMessage>(t =>
 80 
 81                 {
 82 
 83                     var response = t.Result;
 84 
 85                     response.Headers.Add(AccessControlAllowOrign,
 86 
 87                         request.Headers.GetValues(Origin).FirstOrDefault());
 88 
 89                     response.Headers.Add(AccessControlAllowCredentials, "true");
 90 
 91                     return response;
 92 
 93                 });
 94 
 95             }
 96 
 97             return taskResult;
 98 
 99         }
100 
101         return base.SendAsync(request, cancellationToken);
102 
103     }
104 
105 }

 

使用方式,在Global.asax文件添加

 

 1 protected void Application_Start()
 2 
 3 {
 4 
 5     IOCConfig.RegisterAll();
 6 
 7     AreaRegistration.RegisterAllAreas();
 8 
 9     WebApiConfig.Register(GlobalConfiguration.Configuration);
10 
11     FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
12 
13     RouteConfig.RegisterRoutes(RouteTable.Routes);
14 
15     BundleConfig.RegisterBundles(BundleTable.Bundles);
16 
17     GlobalConfiguration.Configuration.MessageHandlers.Add(new CrosHandler());
18 
19 }

 

 

方法二

 

配置文件中添加如下配置,此方法简单,应对简单的跨域请求

 

<system.webServer>

    <httpProtocol>

      <customHeaders>

        <add name="Access-Control-Allow-Origin" value="*" />

        <add name="Access-Control-Allow-Headers" value="Content-Type" />

        <add name="Access-Control-Allow-Methods" value="GET, POST,OPTIONS" />

      </customHeaders>

    </httpProtocol>

<system.webServer>

原文:简玄冰.com/jianxuanbing/p/7324929.html

参考文献:https://code.msdn.microsoft.com/windowsdesktop/Implementing-CORS-support-a677ab5d#content

posted @ 2017-11-14 11:56  Cein  阅读(426)  评论(0编辑  收藏  举报