80

我已经设置了一个从 MVC 4/Web API 模板开始的 ASP.NET Web 应用程序。似乎事情进展得很好——我不知道有什么问题。我使用 Chrome 和 Firefox 浏览该网站。我已经使用 Fiddler 进行了测试,所有的反应似乎都在赚钱。

所以现在我继续编写一个简单的 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 Method Not Allowed。

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 Method Not Allowed 错误,但响应完全为空 - 没有返回数据:

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 方法,或者控制器是否是放置代码的正确位置。奇怪(对我来说)Web API 站点在从 Firefox 或 Chrome 查看时会正确响应,但上面的 .ajax 调用会出错。如何处理 .ajax 代码中的“预检”检查?也许我应该在客户端的 .ajax 逻辑上解决这个问题?或者,如果这是由于未处理 OPTIONS 动词而导致的服务器端问题。

任何人都可以帮忙吗?这一定是一个非常普遍的问题,如果在这里得到回答,我深表歉意。我搜索但没有找到任何有帮助的答案。

更新 恕我直言,这是一个客户端问题,与上面的 Ajax JQuery 代码有关。我这样说是因为当我从 Web 浏览器访问 mywebapidomain/api/user 时,Fiddler 没有显示任何 405 错误标头。我可以复制这个问题的唯一地方是来自 JQuery .ajax() 调用。此外,上面相同的 Ajax 调用在服务器(相同域)上运行时也能正常工作。

我发现了另一篇文章:Prototype AJAX request being sent as OPTIONS 而不是 GET;导致似乎相关的 501 错误,但我已经修改了他们的建议但没有成功。显然,JQuery 是这样编码的,因此如果 Ajax 请求是跨域的(我的请求是跨域的),它会添加几个以某种方式触发 OPTIONS 标头的标头。

'X-Requested-With': 'XMLHttpRequest',
'X-Prototype-Version': Prototype.Version,

似乎应该有比在 JQuery 中修改核心代码更好的解决方案......

下面提供的答案假定这是一个服务器端问题。也许吧,我猜,但我倾向于客户,打电话给托管服务提供商也无济于事。

4

11 回答 11

94

Mike Goodwin 的回答很棒,但是当我尝试它时,它似乎是针对 MVC5/WebApi 2.1 的。Microsoft.AspNet.WebApi.Cors 的依赖项在我的 MVC4 项目中表现不佳。

使用 MVC4 在 WebApi 上启用 CORS 的最简单方法如下。

请注意,我已经允许所有,我建议您将 Origin 限制为您希望您的 API 服务的客户端。允许一切都是安全风险。

网络配置:

<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:

我们这样做是为了允许 OPTIONS http 动词

 public class BaseApiController : ApiController
  {
    public HttpResponseMessage Options()
    {
      return new HttpResponseMessage { StatusCode = HttpStatusCode.OK };
    }
  }
于 2014-01-30T14:14:29.833 回答
52

正如 Daniel A. White 在他的评论中所说,OPTIONS 请求很可能是由客户端创建的,作为跨域 JavaScript 请求的一部分。这是由跨域资源共享 (CORS) 兼容的浏览器自动完成的。该请求是一个初步或飞行前请求,在实际 AJAX 请求之前发出,以确定 CORS 支持哪些请求动词和标头。服务器可以选择不支持、支持全部或部分 HTTP 动词。

为了完成这幅图,AJAX 请求有一个额外的“Origin”标头,它标识了托管 JavaScript 的原始页面是从哪里提供的。服务器可以选择支持来自任何来源的请求,或者只支持一组已知的可信来源。允许任何来源都是一种安全风险,因为这会增加跨站请求伪造 (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 控制器或全局级别时,支持的 HTTP 动词
  • 服务器是否支持使用跨域请求发送凭据

一般来说,这可以正常工作,但您需要确保了解安全风险,尤其是当您允许来自任何域的跨源请求时。在允许这样做之前,请仔细考虑。

至于哪些浏览器支持 CORS,维基百科说以下引擎支持它:

  • 壁虎 1.9.1 (火狐 3.5)
  • WebKit(Safari 4、Chrome 3)
  • MSHTML/Trident 6 (IE10) 部分支持 IE8 和 9
  • Presto(歌剧 12)

http://en.wikipedia.org/wiki/Cross-origin_resource_sharing#Browser_support

于 2013-09-30T18:02:53.693 回答
24

只需将其添加到您的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 请求(如果您愿意,您可以只允许逗号分隔的 Origins/url 列表)。

我使用当前客户端来源而不是*因为这将允许凭据 => 设置Access-Control-Allow-Credentials为 true 将启用跨浏览器会话管理

您还需要在您的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>

希望这可以帮助

于 2015-10-29T07:45:46.107 回答
18

在 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 API 配置:

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。

于 2016-05-24T23:52:44.013 回答
11

我已经设法克服了仅由 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 需要设置为集成模式。

于 2016-09-13T14:38:27.793 回答
8

我有同样的问题。对我来说,解决方法是从 jQuery AJAX 调用中删除自定义内容类型。自定义内容类型触发飞行前请求。我找到了这个:

如果满足以下条件,浏览器可以跳过预检请求:

请求方法是GET, HEAD, or POST, and

应用程序没有设置除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 (在“预检请求”下)

于 2014-01-23T20:58:53.107 回答
3

在 ASP.NET web api 2 中,添加了 CORS 支持。请检查链接 [ http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api ]

于 2015-10-08T12:54:14.353 回答
2
    protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 405 && Context.Request.HttpMethod == "OPTIONS" )
        {
            Response.Clear();
            Response.StatusCode = 200;
            Response.End();
        }
    }
于 2015-09-29T09:24:11.087 回答
1

我也面临同样的问题。

按照以下步骤解决浏览器中的 (CORS) 合规性问题。

使用 Cors 参考将 REDRock 包含在您的解决方案中。包括对 Web API 解决方案的 WebActivatorEx 参考。

然后在 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。

于 2014-05-19T14:29:26.580 回答
1

我遇到了同样的问题,这就是我解决它的方法:

只需将其放入您的 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>
于 2015-12-05T13:23:38.373 回答
0
//In the Application_OnBeginRequest method in GLOBAL.ASX add the following:-  

var res = HttpContext.Current.Response;  
var req = HttpContext.Current.Request;  
res.AppendHeader("Access-Control-Allow-Origin", "*");  
res.AppendHeader("Access-Control-Allow-Credentials", "true");  
res.AppendHeader("Access-Control-Allow-Headers", "Authorization");  
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();
    }

//Remove any entries in the custom headers as this will throw an error that there's to  
//many values in the header.  

<httpProtocol>
    <customHeaders>
    </customHeaders>
</httpProtocol>
于 2020-08-30T11:56:17.333 回答