创建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对象(PONO)并让您使用它。事实上,当我在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之外的其他内容发布消息。 (有notes in stack overflow about this)。
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"
});
这是fiddler报告的内容:(注意它不是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(此内容类型)。
关于修复此问题的所有文章都是关于editing GLOBAL.ASAX的,如果您使用的是ASP.NET,但如果您正在进行自托管WCF则无用。
所以现在你看到了这个问题(对不起这么长时间的啰嗦,但我想把这篇文章做成一篇完整的文章,以便其他人可以关注结果)。
答案 0 :(得分:25)
好的,现在有一些真正的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论坛,堆栈溢出,博客),我希望这会帮助其他人尝试做这些类型的项目。
答案 1 :(得分:2)
Dr.YSG的答案的另一个补充,如果您需要在将POSTS作为单个ID的端点上支持OPTIONS方法,则必须实现多个GetOptions方法:
[WebInvoke(Method = "OPTIONS", UriTemplate = "")]
void GetOptions();
[WebInvoke(Method = "OPTIONS", UriTemplate = "{id}")]
void GetOptions(string id);
WCF /微软无法根据端点的签名自动生成正确的OPTIONS响应,实在令人失望,但至少可以手动处理。
答案 2 :(得分:0)
除了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类似乎解决了这个问题。
答案 3 :(得分:0)
经过多天搜索并阅读了很多帖子和提出的解决方案,我认为YSG博士的这个问题是了解和解决我的问题的最佳资源:angular / wcf / post / CORS等。
但真正对我有用的是:
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (Request.HttpMethod == "OPTIONS")
{
Response.End();
}
}
我知道它不是一个完整的(也不是漂亮的)解决方案,因为我使用的是global.asax并且有很多可能的场景,但我只是想分享这个替代方案,因为它最终可能会帮助其他人。
(除了添加标题)