启用从 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() 函数中的隐藏列值