如何使用WCF自托管处理Ajax JQUERY POST请求

创建RESTful WCF服务器有很多原因(很容易),如果你可以避免使用ASP及其安全框(如果你所做的只是返回信息的简单请求),那就更好了。 请参阅: http : //msdn.microsoft.com/en-us/library/ms750530.aspx了解如何执行此操作。

我发现处理AJAX(JQUERY)GET请求很容易。 但是在POST中处理JSON很棘手。

以下是简单GET请求合同的示例:

[OperationContract] [WebGet(ResponseFormat = WebMessageFormat.Json)] String Version(); 

实现就在这里(返回一个JSON)

  public partial class CatalogService : ICatalogService { public String Version() { mon.IsActive = true; this.BypassCrossDomain(); ViewModel.myself.TransactionCount++; return ViewModel.myself.VersionString; } } 

啊,但如果你想发布一些JSON怎么办? 你会发现很多关于堆栈溢出的文章,告诉你所要做的就是:

  [OperationContract] [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] BuildResponse BuildToby(BuildRequest request); 

它将接收JSON消息,反序列化为Plain .NET对象(PO​​NO)并让您使用它。 事实上,当我在Fiddler中构建请求时,这很好。

 POST /BuildToby HTTP/1.1 User-Agent: Fiddler Content-Type: application/json Host: localhost:4326 Content-Length: 1999 

但是,当您在JQUERY 1.8中使用以下AJAX时,您会发现一个惊喜:

通过指定“application / json”的内容类型,您会发现浏览器会触发“预检”检查,以查看您是否可以发布除了www-url-encloded post消息之外的其他内容。 ( 堆栈中有关于此溢出的注释 )。

  var request = JSON.stringify({ FrameList: ExportData.buildList }); var jqxhr = $.ajax({ type: "POST", url: "http://localhost:4326/BuildToby", data: request, contentType: "application/json; charset=utf-8", dataType: "json" }); 

这里是小提琴报告:(注意它不是POST消息,而是OPTIONS消息)。

 OPTIONS http://localhost:4326/BuildToby HTTP/1.1 Host: localhost:4326 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:19.0) Gecko/20100101 Firefox/19.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://ysg4206 Access-Control-Request-Method: POST Access-Control-Request-Headers: content-type Connection: keep-alive Pragma: no-cache Cache-Control: no-cache 

发生的事情是浏览器(在这种情况下是Firefox)必须使用OPTIONS HTTP消息对服务器进行额外调用,以查看是否允许POST(此内容类型)。

所有关于修复它的文章都是关于编辑GLOBAL.ASAX ,如果你在ASP.NET中这很好,但是如果你正在做一个自主WCF则没用。

所以现在你看到了这个问题(对不起这么长时间的啰嗦,但我想把这篇文章做成一篇完整的文章,以便其他人可以关注结果)。

好的,现在有一些真正的MSDN专家,他们有书面解决方案,但我无法弄清楚: http : //blogs.msdn.com/b/carlosfigueira/archive/2012/05/15/implementing-cors-support -in-wcf.aspx

但我想出了一个简单的解决方案。 至少在WCF 4.5中,您可以添加自己的OperationContract来处理OPTIONS请求:

  [OperationContract] [WebInvoke(Method = "OPTIONS", UriTemplate = "*")] void GetOptions(); 

请注意,方法签名是void,并且没有参数。 首先调用它,然后调用POST消息。

GetOptions的实现是:

  public partial class CatalogService : ICatalogService { public void GetOptions() { mon.IsActive = true; WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*"); WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Methods", "POST, GET, OPTIONS"); WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Headers", "Content-Type"); } } 

这就是你所要做的一切。

您可能还想将此属性添加到服务类中,以便可以序列化大型JSON:

 //This defines the base behavior of the CatalogService. All other files are partial classes that extend the service [ServiceBehavior(MaxItemsInObjectGraph = 2147483647)] // Allows serialization of very large json structures public partial class CatalogService : ICatalogService { PgSqlMonitor mon = new PgSqlMonitor(); private void BypassCrossDomain() { WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*"); } } 

注意我有一个名为BypassCrossDomain()的辅助方法,我调用所有的POST和GET方法,以便我可以处理跨域调用。

我在这里花了很多研究时间(在MSDN论坛,堆栈溢出,博客),我希望这将有助于其他人尝试做这些类型的项目。

Dr.YSG答案的另一个补充,如果你需要在端点上支持将POSTS作为单个ID的OPTIONS方法,你将不得不实现多个GetOptions方法:

  [WebInvoke(Method = "OPTIONS", UriTemplate = "")] void GetOptions(); [WebInvoke(Method = "OPTIONS", UriTemplate = "{id}")] void GetOptions(string id); 

令人失望的是,WCF / Microsoft无法自动生成基于端点签名的正确OPTIONS响应,但至少可以手动处理。

除了Dr.YSG列出的答案之外,我发现我收到了Firefox的通知,表示正在进行重定向,并且出现“405 Method Not Allowed”错误。 添加

 WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", "*"); WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Max-Age", "1728000"); 

到GetOptions类似乎已经解决了这个问题。

经过多天搜索并阅读了许多post和提出的解决方案,我认为YSG博士的这个问题是了解和解决我的问题的最佳资源:angular / wcf / post / CORS等。

但真正对我有用的是:

 protected void Application_BeginRequest(object sender, EventArgs e) { if (Request.HttpMethod == "OPTIONS") { Response.End(); } } 

我知道它不是一个完整的(也不是很漂亮的)解决方案,因为我使用的是global.asax并且有很多可能的场景,但我只是想分享这个替代方案,因为它最终可能会帮助其他人。

(除了添加标题)