我在使用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
http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api 没有那么复杂。
这篇文章最早我就看了,上面的方法也是试过的,但是不行。
@Ixitly: 不行?你的ajax请求,具体错误是什么?
@幻天芒: 不行,我问题里面那段代码就是照着这篇文章写的,最后错误在我追加问题里面写了。
在网上找了找,好像有不少人说,跨域时候在headers里面加入数据,会改变method。
http://stackoverflow.com/questions/12458444/enabling-cross-origin-resource-sharing-on-iis7/14631068#14631068
我还试了这篇文章,但是不起作用。我的请求根本不会触发Application_BeginRequest事件
@Ixitly: 我公司用的是ServiceStack这个的修改版来实现的API。加入header之后,不是变成options,是会先发起一个option来验证下,是否允许你真正的请求类型,如果通过才发起你真正的请求。所以api要同时允许options和你真正的请求类型。
@幻天芒: 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: 看了下你的问题补充,发现你的后端API已经允许跨域了。现在的问题,似乎是出在请求上了。那个Allow,就是api允许的跨域请求类型。
@Ixitly: http://frontenddev.org/article/ajax-browser-cross-domain-request-access-control.html 看参考
@幻天芒: 谢谢,这篇文章我中午看过了,我也在服务器端做了处理在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: 额~你的服务端从你的响应来看,是没问题的。多检查下客户端的ajax写法是否有误。比如jquery版本什么的。
@幻天芒: 经过在网上查资料,服务器端对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: 你的ajax请求也收到了服务端的响应了哇。
@幻天芒: 是收到了options的预请求的响应,但是不进行了,纠结,stackoverflow上好几篇文章都在说这个,好像是IIS给阻止了,我照着上面的配置还是收不到响应。
@Ixitly: 理论上应该是客户端发起,IIS不应该会阻止哇。我这边没遇到过这个问题~
@幻天芒: 终于解决了,是stackoverflow上说的是对的,要移除WebDav和OPTIONSVerbHandler,这两个handler会有对options的特殊处理。
谢谢这一个多礼拜和我一起解决问题:)
@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
@幻天芒: 因为IIS的原因,WebDav和OPTIONSVerbHandler里面会对OPTIONS请求有特殊处理,会截断后面的请求。
@Ixitly: 请求的客户端发起的,这个截断一说有点问题,我觉得最大的可能就是对options的响应做了手脚,导致客户端不发后面的请求了。
@Ixitly: 能问下最后是怎么解决的吗?
@wulipeng1987: 在web.config里面移除WebDav和OPTIONSVerbHandler,这两个handler会有对options的特殊处理。
@幻天芒: 谢谢,帮了大忙,不知道回复谁,加上
<remove name="WebDav" />
<remove name="OPTIONSVerbHandler" />
这两个到webconfig里终于在iis里正常了。
这个最后能解决么?我也是用的web api + 前端请求自定义header, get用了jsonp,发现jsonp不支持post请求,疯了。然后发现post请求+add headers ,请求会改成options,把后台的方法请求类型改成HttpOptions可以正常请求,但是前端不会进入success回调,而是进入error回调。
请问你们是怎么解决的呢