如何在ASP.NET MVC / WebAPI应用程序中支持HTTP OPTIONS动词
我已经安装了一个ASP.NET MVC 4 / Web API模板开始的ASP.NET Web应用程序。 似乎事情真的很好 – 没有我意识到的问题。 我使用Chrome和Firefox浏览网站。 我已经使用提琴手testing,所有的回应似乎是在钱上。
所以现在我继续编写一个简单的Test.aspx来使用这个新的Web API。 脚本的相关部分:
<script type="text/javascript"> $(function () { $.ajax({ url: "http://mywebapidomain.com/api/user", type: "GET", contentType: "json", success: function (data) { $.each(data, function (index, item) { .... }); } ); }, failure: function (result) { alert(result.d); }, error: function (XMLHttpRequest, textStatus, errorThrown) { alert("An error occurred, please try again. " + textStatus); } }); }); </script>
这会生成一个REQUEST标头:
OPTIONS http://host.mywebapidomain.com/api/user HTTP/1.1 Host: host.mywebapidomain.com User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Origin: http://mywebapidomain.com Access-Control-Request-Method: GET Access-Control-Request-Headers: content-type Connection: keep-alive
原样,Web API返回一个不允许的405方法。
HTTP/1.1 405 Method Not Allowed Cache-Control: no-cache Pragma: no-cache Content-Type: application/xml; charset=utf-8 Expires: -1 Server: Microsoft-IIS/8.0 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Date: Mon, 30 Sep 2013 13:28:12 GMT Content-Length: 96 <Error><Message>The requested resource does not support http method 'OPTIONS'.</Message></Error>
我知道OPTIONS谓词在Web API控制器中默认没有连接…所以,我把下面的代码放在我的UserController.cs中:
// OPTIONS http-verb handler public HttpResponseMessage OptionsUser() { var response = new HttpResponseMessage(); response.StatusCode = HttpStatusCode.OK; return response; }
…这消除了405方法不允许错误,但响应是完全空的 – 没有数据返回:
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Expires: -1 Server: Microsoft-IIS/8.0 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Date: Mon, 30 Sep 2013 12:56:21 GMT Content-Length: 0
必须有额外的逻辑…我不知道如何正确编写Options方法,或者如果控制器甚至是放置代码的适当位置。 奇怪(对我来说)从Firefox或Chrome浏览器查看Web API网站的响应是正确的,但上面的错误.ajax调用。 如何处理.ajax代码中的“预检”检查? 也许我应该在客户端的.ajax逻辑解决这个问题? 或者,如果由于不处理OPTIONS动词而导致服务器端出现这个问题。
谁能帮忙? 这是一个非常普遍的问题,如果在这里得到了答复,我很抱歉。 我搜查,但没有find任何帮助的答案。
更新恕我直言,这是一个客户端问题,并与上面的Ajax JQuery代码有关。 我这样说是因为当我从Web浏览器访问mywebapidomain / api / user时,Fiddler不显示任何405错误标题。 我唯一可以复制这个问题的地方是来自JQuery .ajax()调用。 另外,上面相同的Ajax调用在服务器(相同的域)上运行时工作正常。
我发现另一个post: 原型AJAX请求作为OPTIONS而不是GET发送; 导致501错误 ,似乎是相关的,但我没有成功与他们的build议修补。 显然,JQuery是这样编码的,如果一个Ajax请求是跨域的(我的是),它会添加一些标题,以某种方式触发OPTIONS标题。
'X-Requested-With': 'XMLHttpRequest', 'X-Prototype-Version': Prototype.Version,
似乎应该有更好的解决scheme可用比在JQuery中修改核心代码…
下面提供的答案假设这是一个服务器端问题。 也许,我想,但我倾向于客户,并呼吁托pipe服务提供商不会帮助。
正如Daniel A. White在评论中所说,OPTIONS请求很可能是由客户端创build的,作为跨域JavaScript请求的一部分。 这是由跨源资源共享(CORS)兼容的浏览器自动完成的。 该请求是在实际的AJAX请求之前做出的初步或预先请求,以确定CORS支持哪些请求动词和头文件。 服务器可以select不支持全部或部分HTTP动词。
为了完成这个图片,AJAX请求还有一个额外的“Origin”头,它标识了托pipeJavaScript的原始页面来自哪里。 服务器可以select支持来自任何来源的请求,或者仅仅为一组已知的,可信的来源。 允许任何来源是安全风险,因为可能会增加跨站点请求伪造(CSRF)的风险。
所以,你需要启用CORS。
这里是一个链接,说明如何在ASP.Net Web API中执行此操作
http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api#enable-cors
在那里描述的实现允许你指定,除其他外
- CORS支持每个操作,每个控制器或全球基础
- 支持的起源
- 当启用CORS aa控制器或全局级别时,支持的HTTP动词
- 服务器是否支持使用跨源请求发送凭证
一般来说,这个工作正常,但是你需要确保你知道安全风险,特别是如果你允许来自任何域的交叉源请求。 在允许之前仔细考虑。
在哪些浏览器支持CORS方面,维基百科表示,以下引擎支持它:
- 壁虎1.9.1(FireFox 3.5)
- WebKit(Safari 4,Chrome 3)
- MSHTML / Trident 6(IE10),部分支持IE8和9
- Presto(Opera 12)
http://en.wikipedia.org/wiki/Cross-origin_resource_sharing#Browser_support
Mike Goodwin的回答非常好,但是当我尝试时,似乎是针对MVC5 / WebApi 2.1。 Microsoft.AspNet.WebApi.Cors的依赖关系与我的MVC4项目无法很好地发挥作用。
使用MVC4在WebApi上启用CORS的最简单的方法如下。
请注意,我已经允许所有的,我build议你限制Origin的只是你想要你的API服务的客户端。 允许一切都是安全风险。
Web.config文件:
<system.webServer> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="*" /> <add name="Access-Control-Allow-Methods" value="GET, PUT, POST, DELETE, HEAD" /> <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" /> </customHeaders> </httpProtocol> </system.webServer>
BaseApiController.cs:
我们这样做是为了允许选项http动词
public class BaseApiController : ApiController { public HttpResponseMessage Options() { return new HttpResponseMessage { StatusCode = HttpStatusCode.OK }; } }
只需将其添加到Application_OnBeginRequest
方法(这将为您的应用程序全局启用CORS支持)并“处理”预检请求:
var res = HttpContext.Current.Response; var req = HttpContext.Current.Request; res.AppendHeader("Access-Control-Allow-Origin", req.Headers["Origin"]); res.AppendHeader("Access-Control-Allow-Credentials", "true"); res.AppendHeader("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Date, X-Api-Version, X-File-Name"); res.AppendHeader("Access-Control-Allow-Methods", "POST,GET,PUT,PATCH,DELETE,OPTIONS"); // ==== Respond to the OPTIONS verb ===== if (req.HttpMethod == "OPTIONS") { res.StatusCode = 200; res.End(); }
*安全:请注意,这将使得从任何地方的Ajax请求到您的服务器(如果您愿意,您可以只允许逗号分隔的起源/ URL列表)。
我使用当前的客户端来源而不是*
因为这将允许credentials =>将Access-Control-Allow-Credentials
为true将启用跨浏览器会话pipe理
你也需要在你的webconfig
部分system.webServer
启用删除并放入修补程序和选项动词,否则IIS将阻止它们:
<handlers> <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>
希望这可以帮助
我已经设法克服了只有在global.asax中的自定义代码之前抛出的飞行前ajax选项请求上的405和404错误
protected void Application_BeginRequest() { HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*"); if (HttpContext.Current.Request.HttpMethod == "OPTIONS") { //These headers are handling the "pre-flight" OPTIONS call sent by the browser HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "GET, OPTIONS"); HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept"); HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000"); HttpContext.Current.Response.End(); } }
PS:考虑到所有的安全问题*。
我必须禁用CORS,因为它返回“Access-Control-Allow-Origin”标头包含多个值。
在web.config中也需要这个:
<handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0"/> <remove name="OPTIONSVerbHandler"/> <remove name="TRACEVerbHandler"/> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0"/> </handlers>
而app.pool需要设置为集成模式。
我有这个相同的问题。 对我来说,解决的办法是从jQuery AJAX调用中移除自定义的内容types。 自定义内容types会触发提前请求。 我find了这个:
如果满足以下条件,则浏览器可以跳过预检请求:
请求方法是
GET
,HEAD
或POST
, 并且除了
Accept
,Accept-Language
,Content-Language
,Content-Type
或Last-Event-ID
,应用程序不设置任何请求标头,
Content-Type
头(如果设置)是以下之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
从此页面: http : //www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api (在“预检请求”下)
在Web API 2项目中遇到同样的问题(因为不值得进入的原因而无法使用标准的CORS包),我可以通过实现一个自定义的DelagatingHandler来解决这个问题:
public class AllowOptionsHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { var response = await base.SendAsync(request, cancellationToken); if (request.Method == HttpMethod.Options && response.StatusCode == HttpStatusCode.MethodNotAllowed) { response = new HttpResponseMessage(HttpStatusCode.OK); } return response; } }
对于Web APIconfiguration:
config.MessageHandlers.Add(new AllowOptionsHandler());
请注意,我也在Web.config中启用了CORS头文件,类似于这里发布的其他答案:
<system.webServer> <modules runAllManagedModulesForAllRequests="true"> <remove name="WebDAVModule" /> </modules> <httpProtocol> <customHeaders> <add name="Access-Control-Allow-Origin" value="*" /> <add name="Access-Control-Allow-Headers" value="accept, cache-control, content-type, authorization" /> <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS" /> </customHeaders> </httpProtocol> <handlers> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="TRACEVerbHandler" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers> </system.webServer>
请注意,我的项目不包括MVC,只有Web API 2。
在ASP.NET web api 2中,添加了CORS支持。 请检查链接[ http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api ]
我也面临同样的问题。
按照以下步骤解决浏览器中(CORS)合规问题。
使用Cors参考在您的解决scheme中包含REDRock。 包括WebActivatorEx参考Web API解决scheme。
然后在Web API App_Start文件夹中添加文件CorsConfig。
[assembly: PreApplicationStartMethod(typeof(WebApiNamespace.CorsConfig), "PreStart")] namespace WebApiNamespace { public static class CorsConfig { public static void PreStart() { GlobalConfiguration.Configuration.MessageHandlers.Add(new RedRocket.WebApi.Cors.CorsHandler()); } } }
完成这些更改后,我可以访问所有浏览器中的webapi。
protected void Application_EndRequest() { if (Context.Response.StatusCode == 405 && Context.Request.HttpMethod == "OPTIONS" ) { Response.Clear(); Response.StatusCode = 200; Response.End(); } }
我有同样的问题,这是我如何解决它:
只要把这个在你的web.config中:
<system.webServer> <modules> <remove name="WebDAVModule" /> </modules> <httpProtocol> <customHeaders> <add name="Access-Control-Expose-Headers " value="WWW-Authenticate"/> <add name="Access-Control-Allow-Origin" value="*" /> <add name="Access-Control-Allow-Methods" value="GET, POST, OPTIONS, PUT, PATCH, DELETE" /> <add name="Access-Control-Allow-Headers" value="accept, authorization, Content-Type" /> <remove name="X-Powered-By" /> </customHeaders> </httpProtocol> <handlers> <remove name="WebDAV" /> <remove name="ExtensionlessUrlHandler-Integrated-4.0" /> <remove name="TRACEVerbHandler" /> <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers> </system.webServer>