启用从 jQuery ajax 到不在 IIS 中托管的 WCF 服务的 CORS POST

Posted

技术标签:

【中文标题】启用从 jQuery ajax 到不在 IIS 中托管的 WCF 服务的 CORS POST【英文标题】:Enable CORS POST from jQuery ajax to WCF Service NOT Hosted in IIS 【发布时间】:2017-07-28 04:15:32 【问题描述】:

我需要向可执行文件托管的 WCF 服务发出跨域 POST 请求,而不是来自 IIS。目的是从 Web 客户端传入一个 id 字符串数组,WCF 服务将使用它来查找和返回与这些 id 相关的日志。

我相信我已经接近了,我缺少的主要部分是 app.config 文件中的一个配置,它添加了一个自定义标头。我会注意到,在我的解决方案的其他地方,有一个 web.config 用于描述我正在寻找的行为的另一个服务,其中包括:

<httpProtocol>
  <customHeaders>
    <!-- http://***.com/questions/12458444/enabling-cross-origin-resource-sharing-on-iis7 -->
    <add name="Access-Control-Allow-Headers" value="X-Requested-With,Content-Type,Accept" />
    <add name="Access-Control-Allow-Methods" value="GET,PUT,POST,DELETE,OPTIONS" />
    <add name="Access-Control-Allow-Credentials" value="true" />
    <add name="Timing-Allow-Origin" value="*" />
  </customHeaders>
</httpProtocol>

现在我对 above 代码的问题是它驻留在 IIS 托管服务的 web.config 文件中。我需要能够在 app.config 中为我在可执行文件中托管的服务描述该自定义标头(我在下面提供了相关方法)。

我已经尝试解决这个问题好几天了,到目前为止,除了我正在寻求帮助的配置混乱之外,我的理解是这种语法可以调用服务:

        var settings: JQueryAjaxSettings = ;

        settings.async = true;
        settings.crossDomain = true;
        settings.url = this.getLogEntriesByIdResource;
        settings.method = "POST";
        settings.type = "POST";
        settings.dataType = 'JSON';
        settings.headers = 
            "content-type": "application/json",
            "cache-control": "no-cache"
        
        settings.data = JSON.stringify(ids);

        jQuery.ajax(settings).done(function (response) 
            console.log(response);
        );

这是在上面代码调用的 WCF 端点上的一个很好的传递:

    [OperationContract, CorsEnabled]
    [WebInvoke(Method = "POST", BodyStyle = WebMessageBodyStyle.Bare,
        RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json,
        UriTemplate = "GetLogEntriesById")]
    [Description("Request entities or entity updates for a particular time and place")]
    public List<LogEntryInfo> GetLogEntriesById(string[] ids)
    
        List<LogEntryInfo> results = new List<LogEntryInfo>();
        List<ILogEntry> rels = Gateway.MongoDbInstance.ReturnLogEntries(ids.ToList());
        rels.ForEach(e => results.Add(new LogEntryInfo(e)));
        return results;
    

另外,这里是设置我的 webServiceHost 的代码:

    public LogEntryRestService(string baseAddress, string endpoint)
    
        _baseAddress = baseAddress;
        _endpoint = endpoint;

        _webServiceHost = new WebServiceHost(this, new Uri(baseAddress));

        var webBinding = new WebHttpBinding(WebHttpSecurityMode.None)  CrossDomainScriptAccessEnabled = true ;
        ServiceEndpoint ep = _webServiceHost.AddServiceEndpoint(typeof(LogEntryRestService), webBinding, endpoint);

        WebHttpBehavior webBehavior = new WebHttpBehavior();
        webBehavior.HelpEnabled = true;

        ep.EndpointBehaviors.Add(webBehavior);
        ep.EndpointBehaviors.Add(new EnableCorsEndpointBehavior());

        Program.LogMessage(string.Format("Initializing LogEntry REST endpoint BaseAddress: 0 EndpointName: 1",
                _webServiceHost.BaseAddresses[0], endpoint));

        _webServiceHost.Open();
    

我还要指出,来自 Postman 的标准调用(带有指定给端点的 JSON 正文)运行良好。我知道 Postman(Chrome 扩展程序)不像网页那样遵守 CORS。我的调用 ajax 代码与 Postman 为 jQuery ajax 提供的建议调用代码相同,所以我认为这意味着我遇到了 CORS 问题。

我对所有这些配置和 CORS 绥靖感到有些疲惫,我真的很感谢新的眼睛和帮助!

更新

所以我尝试将我当前的代码(我在上面分享的内容)部署到一个实际的服务器实例,而不是在我的本地机器上进行调试,并发现一切正常。但是,我 am 仍然在本地收到我想解决的错误。当我的本地调试客户端调用我的本地调试服务时,此响应描述了该错误:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Service</title>
    <style>BODY  color: #000000; background-color: white; font-family: Verdana; margin-left: 0px; margin-top: 0px;  #content  margin-left: 30px; font-size: .70em; padding-bottom: 2em;  A:link  color: #336699; font-weight: bold; text-decoration: underline;  A:visited  color: #6699cc; font-weight: bold; text-decoration: underline;  A:active  color: #336699; font-weight: bold; text-decoration: underline;  .heading1  background-color: #003366; border-bottom: #336699 6px solid; color: #ffffff; font-family: Tahoma; font-size: 26px; font-weight: normal;margin: 0em 0em 10px -20px; padding-bottom: 8px; padding-left: 30px;padding-top: 16px; pre  font-size:small; background-color: #e5e5cc; padding: 5px; font-family: Courier New; margin-top: 0px; border: 1px #f0f0e0 solid; white-space: pre-wrap; white-space: -pre-wrap; word-wrap: break-word;  table  border-collapse: collapse; border-spacing: 0px; font-family: Verdana; table th  border-right: 2px white solid; border-bottom: 2px white solid; font-weight: bold; background-color: #cecf9c; table td  border-right: 2px white solid; border-bottom: 2px white solid; background-color: #e5e5cc;</style>
  </head>
  <body>
    <div id="content">
      <p class="heading1">Service</p>
      <p xmlns="">Method not allowed. Please see the <a rel="help-page" href="http://localhost/LogEntryService/help">service help page</a> for constructing valid requests to the service.</p>
    </div>
  </body>
</html>                                

我还要注意,请求方法显示为“OPTIONS”而不是 POST,这是它应该是的,也是我在专用服务器上运行所有这些代码时看到的行为。

POST 方法默认为 OPTIONS 方法的任何原因?我假设我回到了原点,为了解决这个问题,我需要在我的非 IIS 托管 Web 服务上启用 CORS。再次感谢您的帮助或见解!

更新

我在屏幕截图下方包含了请求的提琴手检查和请求的 Chrome 开发工具网络视图。

提琴手检查^

Chrome 开发工具请求和响应^

【问题讨论】:

我不是 jquery 专家,但 content-type: application/json 会触发预检响应,我知道。你有错误吗??我没有在您的帖子中看到任何错误。 哦,我应该添加我的错误,感谢您指出这一点。而且我认为你是对的,尽管我知道我需要以某种方式指定 JSON 内容类型,因为这就是我计划将字符串数组传递给我的 web 方法的方式。自从最初发布以来,我也取得了一些进展,我现在将更新我的帖子。感谢您的观看! 你有fiddler吗?我会查看提琴手的流量。如果您的标头发生变化,则很可能意味着您没有发送自定义标头,并且最终会得到一个默认标头,该标头可能会更改您发送的值(尽管我不知道这是否会影响该方法)。如果发生这种情况,您需要彻底确认应用程序中的 cors 配置。也不要以html格式发布您的错误消息,请发布错误消息。 发布您尝试发送的帖子的小提琴检查员。 邮递员不会有 cors 问题。我相信它会欺骗另一个域中的应用程序。 fiddler 是您的应用程序调用您的 Web 服务时发生的事情的真实故事。你看到中间。向我展示一个有助于诊断的原始小提琴请求或响应。​​ 【参考方案1】:

这应该可以帮助您找出问题所在。我看到的屏幕截图(主要是小提琴)是预检。这些是选项。但是如果你通过了预检,你的 Post 应该会被发送出去,所以在那之后应该有另一个条目。这是 Fiddle 中一个好的 Post 示例。

预检(原始):

OPTIONS http://localhost:5000/api/values/dui/ HTTP/1.1
Host: localhost:5000
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://evil.com/
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
Access-Control-Request-Headers: access-control-allow-origin, content-type
Accept: */*
Referer: http://localhost:3000/validator
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,en;q=0.8

紧随其后的是帖子

请求(原始):

POST http://localhost:5000/api/values/dui/ HTTP/1.1
Host: localhost:5000
Connection: keep-alive
Content-Length: 17
access-control-allow-origin: *
accept: application/json
Origin: http://evil.com/
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36
content-type: application/json
Referer: http://localhost:3000/validator
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8

"name":"test"

来自我的 API 的响应(原始):

HTTP/1.1 200 OK
Date: Thu, 09 Mar 2017 16:42:27 GMT
Content-Type: application/json; charset=utf-8
Server: Kestrel
Content-Length: 57

"name":"Hey Test, Im The Dude man.","link":"dude.png"

我认为您没有通过预检。我不确定 Chrome 中的响应来自何处,但您需要确保您已设置为通过预检。因此,您的应用程序中的代码必须具有正确的 cors 配置以及服务器(在 IIS 中使用 web.config)。我在 dotnet core webApi 和 React UI 正确设置 Cors 时遇到了类似的问题(查看 here)。你可以在这里看到我是如何处理它的。希望这可以帮助。但我会专注于预检,确保通过。如果是这样,请转到 Post 本身。例如,如果您在设置的帖子(应用程序/json)中没有看到您的内容类型,则您的自定义标头在某处被破坏。抱歉,我无法准确指出您需要设置它的位置,但选项是预检是正常的。我不会专注于那个。

**********更新解决方案已添加**********

我相信你需要这样的东西来解决你的问题。基本上您需要在 WebService 本身中添加标头。

"添加 global.asax 文件并将以下代码添加到 Application_BeginRequest。以下是代码 sn-p。"

protected void Application_BeginRequest(object sender, EventArgs e)

HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin",    "http://localhost");
if (HttpContext.Current.Request.HttpMethod == "OPTIONS")

    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST, PUT, DELETE");

    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
    HttpContext.Current.Response.AddHeader("Access-Control-Max-Age", "1728000");
    HttpContext.Current.Response.End();


完整参考是here。这篇文章是相当新的。这应该可以解决您的问题。

【讨论】:

是的,你正中了锤子上的钉子。那么我的问题是,我很难让我的服务通过预检并允许以下 POST。非常感谢您的时间和解释! @ThePartyTurtle 查看我发布的解决方案的更新。希望能解决。 不,不幸的是我无法用这种方法解决我的问题:^/。诚然,我不得不暂时把它放在次要位置。幸运的是,就像我提到的那样,生产环境中没有 CORS 问题。当我重新审视这个问题时,我会给你的方法,那篇文章中提到的方法,另一种新的尝试。再次感谢您的帮助!

以上是关于启用从 jQuery ajax 到不在 IIS 中托管的 WCF 服务的 CORS POST的主要内容,如果未能解决你的问题,请参考以下文章

Jquery AJAX ASP.NET IIS 跨域 超简单解决办法

Jquery Datepicker - 从 mysql 数据库启用日期

使用 jQuery AJAX 检索数据而不在 php 中重新加载页面

如何从 jQuery 数据表中的 ajax 数据源获取 mRender() 函数中的隐藏列值

带有 ASP.NET MVC 的 jquery - 调用启用 ajax 的 Web 服务

ASP.NET CORE 3.0 Jquery Ajax请求成功有时不在同一视图中执行