首页 新闻 会员 周边

跨域请求web api,如果headers传递数据,请求无响应。

1
悬赏园豆:100 [已解决问题] 解决于 2015-07-07 10:51

我在使用web api的cors,参照微软官网的demo,写了一个用于cors的特性:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
    public class CorsPolicyAttribute : System.Attribute, ICorsPolicyProvider
    {
        private CorsPolicy _policy;

        public CorsPolicyAttribute()
        {
            _policy = new CorsPolicy
            {
                AllowAnyMethod = true,
                AllowAnyHeader = true
            };

            ////添加允许网站。
            ////_policy.Origins.Add("http://www.example.com");
            var corsOrigins = ConfigHelper.GetAppSettingValue(AttributeConfig.CorsOrigins);
            if (corsOrigins != null)
            {
                if (corsOrigins.Trim() == "*")
                {
                    _policy.AllowAnyOrigin = true;
                }
                else if (string.IsNullOrEmpty(corsOrigins.Trim()))
                {
                    var origins = corsOrigins.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                    foreach (var origin in origins)
                    {
                        _policy.Origins.Add(origin);
                    }
                }
            }
        }

        public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
        {
            return Task.FromResult(_policy);
        }
    }

此代码在headers内无数据时是可以正常工作的,但一旦在headers内增加了数据,则请求无响应。

下面代码是用jquery请求web api

var urls = "http://192.168.0.133/AuthorizationService/api/Auth";
$.ajax(urls, { type:
'post', data: { UserName: $("#userName").val(), Password: $("#password").val(), Token: "", DeviceId: deviceId }, headers: { Authorization_Token: "adac4d7f6d4b78952ddb3b02ccd85434", Authorization_key: "key" }, success: function(data) { if (data.Auth.IsLogin) {

下面是请求的结果

请求:
OPTIONS http://192.168.0.133/AuthorizationService/api/Auth HTTP/1.1
Host: 192.168.0.133
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:33202
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36
Access-Control-Request-Headers: accept, authorization_key, authorization_token, content-type
Accept: */*
Referer: http://localhost:33202/login.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
响应结果:
HTTP/1.1 200 OK
Allow: OPTIONS, TRACE, GET, HEAD, POST
Server: Microsoft-IIS/7.5
Public: OPTIONS, TRACE, GET, HEAD, POST
X-Powered-By: ASP.NET
Date: Thu, 02 Jul 2015 06:27:50 GMT
Content-Length: 0

如果将headers注释掉则正确能够请求。

我找过相关文章http://stackoverflow.com/questions/12409600/error-request-header-field-content-type-is-not-allowed-by-access-control-allow
但是未能解决,请问谁遇到过web api跨域请求时,headers放参数的情况。

PS:不考虑使用JSONP。

问题补充:

在headers不加入自定义数据时,请求的method是正确的

POST http://192.168.0.133/AuthorizationService/api/Auth HTTP/1.1
Host: 192.168.0.133
Connection: keep-alive
Content-Length: 45
Accept: */*
Origin: http://localhost:33202
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Referer: http://localhost:33202/login.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8

但是在headers加入数据之后就变成了OPTIONS

OPTIONS http://192.168.0.133/AuthorizationService/api/Auth HTTP/1.1
Host: 192.168.0.133
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:33202
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36
Access-Control-Request-Headers: accept, authorizationkey, authorizationtoken, content-type
Accept: */*
Referer: http://localhost:33202/login.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
Ixitly的主页 Ixitly | 初学一级 | 园豆:79
提问于:2015-07-02 14:30
< >
分享
最佳答案
0

http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api  没有那么复杂。

收获园豆:100
幻天芒 | 高人七级 |园豆:37175 | 2015-07-02 16:24

这篇文章最早我就看了,上面的方法也是试过的,但是不行。

Ixitly | 园豆:79 (初学一级) | 2015-07-02 16:36

@Ixitly: 不行?你的ajax请求,具体错误是什么?

幻天芒 | 园豆:37175 (高人七级) | 2015-07-02 17:29

@幻天芒: 不行,我问题里面那段代码就是照着这篇文章写的,最后错误在我追加问题里面写了。

在网上找了找,好像有不少人说,跨域时候在headers里面加入数据,会改变method。

http://stackoverflow.com/questions/12458444/enabling-cross-origin-resource-sharing-on-iis7/14631068#14631068

我还试了这篇文章,但是不起作用。我的请求根本不会触发Application_BeginRequest事件

Ixitly | 园豆:79 (初学一级) | 2015-07-02 17:41

@Ixitly: 我公司用的是ServiceStack这个的修改版来实现的API。加入header之后,不是变成options,是会先发起一个option来验证下,是否允许你真正的请求类型,如果通过才发起你真正的请求。所以api要同时允许options和你真正的请求类型。

幻天芒 | 园豆:37175 (高人七级) | 2015-07-03 10:02

@幻天芒: ServiceStack没接触过,是一个通信框架么?我用的是MS的web api框架。

如果加入headers后,先发送option请求去验证,那这个请求应该如何诊断?是否能有代码捕捉到?

请求

OPTIONS http://192.168.0.133/AuthorizationService/api/Auth HTTP/1.1
Host: 192.168.0.133
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://localhost:33202
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.130 Safari/537.36
Access-Control-Request-Headers: accept, authorizationkey, authorizationtoken, content-type
Accept: */*
Referer: http://localhost:33202/login.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8

响应

HTTP/1.1 200 OK
Allow: OPTIONS, TRACE, GET, HEAD, POST
Server: Microsoft-IIS/7.5
Public: OPTIONS, TRACE, GET, HEAD, POST
X-Powered-By: ASP.NET
Date: Fri, 03 Jul 2015 02:16:40 GMT
Content-Length: 0

响应是200状态,但是不会再进行了。

Ixitly | 园豆:79 (初学一级) | 2015-07-03 10:17

@Ixitly: 看了下你的问题补充,发现你的后端API已经允许跨域了。现在的问题,似乎是出在请求上了。那个Allow,就是api允许的跨域请求类型。

幻天芒 | 园豆:37175 (高人七级) | 2015-07-03 10:19

@Ixitly: http://frontenddev.org/article/ajax-browser-cross-domain-request-access-control.html  看参考

幻天芒 | 园豆:37175 (高人七级) | 2015-07-03 11:06

@幻天芒: 谢谢,这篇文章我中午看过了,我也在服务器端做了处理在global里加入了

protected void Application_BeginRequest(object sender, EventArgs e)
        {
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
            if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
            {
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "*");
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "*");
                HttpContext.Current.Response.End();
            }
        }

但是预请求无法走到这段代码,预响应之后就没下文了。

Ixitly | 园豆:79 (初学一级) | 2015-07-03 14:08

@Ixitly: 额~你的服务端从你的响应来看,是没问题的。多检查下客户端的ajax写法是否有误。比如jquery版本什么的。

幻天芒 | 园豆:37175 (高人七级) | 2015-07-03 14:42

@幻天芒: 经过在网上查资料,服务器端对OPTIONS请求能够返回如下信息了。

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Server: Microsoft-IIS/7.5
Access-Control-Allow-Origin: *
Access-Control-Allow-Origin: http://localhost:10004
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: accept,authorizationkey,authorizationtoken,content-type
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Mon, 06 Jul 2015 10:02:59 GMT
Content-Length: 0

Application_BeginRequest事件也能进入了,但之后就没动静了。

我修改了web.config,

  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules runAllManagedModulesForAllRequests="false">
      <remove name="FormsAuthenticationModule" />
      <remove name="WebDavModule" />
      <!--<add name="CorsHttpModule"
         type="Thinktecture.IdentityModel.Http.Cors.IIS.CorsHttpModule"/>-->
    </modules>
    <handlers>
      <remove name="WebDav" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
      <remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
  </system.webServer>
Ixitly | 园豆:79 (初学一级) | 2015-07-06 18:13

@Ixitly: 你的ajax请求也收到了服务端的响应了哇。

幻天芒 | 园豆:37175 (高人七级) | 2015-07-06 22:33

@幻天芒: 是收到了options的预请求的响应,但是不进行了,纠结,stackoverflow上好几篇文章都在说这个,好像是IIS给阻止了,我照着上面的配置还是收不到响应。

Ixitly | 园豆:79 (初学一级) | 2015-07-07 09:27

@Ixitly: 理论上应该是客户端发起,IIS不应该会阻止哇。我这边没遇到过这个问题~

幻天芒 | 园豆:37175 (高人七级) | 2015-07-07 09:46

@幻天芒: 终于解决了,是stackoverflow上说的是对的,要移除WebDav和OPTIONSVerbHandler,这两个handler会有对options的特殊处理。

谢谢这一个多礼拜和我一起解决问题:)

Ixitly | 园豆:79 (初学一级) | 2015-07-07 10:50

@Ixitly: 都没帮上你忙,而且我也学到东西了,也谢谢你~ 还是比较疑惑,为什么你的那个响应在浏览器端,会不再发送真正的请求。我的options请求响应如下:

access-control-allow-headers:accept, content-type
access-control-allow-methods:OPTIONS, GET, PUT, DELETE
access-control-allow-origin:http://xxx
cache-control:private
Connection:keep-alive
content-length:0
date:Tue, 07 Jul 2015 03:02:22 GMT
server:Microsoft-IIS/7.5
x-aspnet-version:4.0.30319
x-powered-by:ASP.NET
幻天芒 | 园豆:37175 (高人七级) | 2015-07-07 11:04

@幻天芒: 因为IIS的原因,WebDav和OPTIONSVerbHandler里面会对OPTIONS请求有特殊处理,会截断后面的请求。

Ixitly | 园豆:79 (初学一级) | 2015-07-07 14:37

@Ixitly: 请求的客户端发起的,这个截断一说有点问题,我觉得最大的可能就是对options的响应做了手脚,导致客户端不发后面的请求了。

幻天芒 | 园豆:37175 (高人七级) | 2015-07-07 15:53

@Ixitly: 能问下最后是怎么解决的吗?

wulipeng1987 | 园豆:200 (初学一级) | 2016-09-06 17:21

@wulipeng1987: 在web.config里面移除WebDav和OPTIONSVerbHandler,这两个handler会有对options的特殊处理。

Ixitly | 园豆:79 (初学一级) | 2016-09-09 14:39

@幻天芒: 谢谢,帮了大忙,不知道回复谁,加上

<remove name="WebDav" />

<remove name="OPTIONSVerbHandler" />

这两个到webconfig里终于在iis里正常了。

大龄愣头青 | 园豆:200 (初学一级) | 2017-03-30 15:12
其他回答(1)
0

这个最后能解决么?我也是用的web api + 前端请求自定义header, get用了jsonp,发现jsonp不支持post请求,疯了。然后发现post请求+add headers ,请求会改成options,把后台的方法请求类型改成HttpOptions可以正常请求,但是前端不会进入success回调,而是进入error回调。
请问你们是怎么解决的呢

lingshanran | 园豆:204 (菜鸟二级) | 2017-09-11 18:18
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册