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

Posted

技术标签:

【中文标题】如何使用 WCF 自托管处理 Ajax JQUERY POST 请求【英文标题】:How to handle Ajax JQUERY POST request with WCF self-host 【发布时间】:2013-02-28 11:04:25 【问题描述】:

创建 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 消息,反序列化为普通 .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"
);

这是提琴手报告的内容:(注意它不是 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,那就没用了。

所以现在你看到了这个问题(对不起,啰嗦了这么多,但我想把这篇文章写成完整的文章,以便其他人可以跟进结果)。

【问题讨论】:

在为此的 Mono 框架中,看起来无论我使用什么,OPTIONS 请求都会无限期挂起。这是您在实施时看到的吗?肯定有一段时间了 【参考方案1】:

好的,现在有一些真正的 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();

请注意,方法签名是无效的,并且没有参数。这将首先被调用,然后将调用 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 论坛、堆栈溢出、博客中),我希望这会帮助其他尝试做这类项目的人。

【讨论】:

谢谢,上面的 BypassCrossDomain 位帮助解决了我遇到的跨域问题。 谢谢,这真的很有帮助。我用它从 http 上下文调用一些 https 服务,效果很好! 似乎只是为我发送一个空白回复!没有标题,什么都没有! 谢谢。它成功了。你从哪里得到这个神奇的 GetOptions?这在任何地方都有记录吗? 如果我能投票 100 次,我会的。这是使用 WCF 构建 AJAX 服务的绝佳解决方案。其他(不那么优雅)的解决方案包括拦截请求和使用 200 代码抛出 HTTP 异常。【参考方案2】:

Dr.YSG 回答的另一个补充是,如果您需要在将 POSTS 发送到单个 ID 的端点上支持 OPTIONS 方法,则必须实现多个 GetOptions 方法:

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

WCF/Microsoft 不能自动根据端点的签名自动生成正确的 OPTIONS 响应,这真是令人失望,但至少可以手动处理。

【讨论】:

是否有用户对此功能的语音请求未在 WCF 中正确实现?【参考方案3】:

除了 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 类似乎已经解决了这个问题。

【讨论】:

对于多年后阅读并发现没有任何效果的任何人,我已经尝试了几个小时的各种选择。直到我将第一行代码(allow-origin)放在我的 WCF 方法的主体中 之前,没有任何效果。 (不在某些 GetOptions() 方法中。)【参考方案4】:

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

但真正对我有用的是:

protected void Application_BeginRequest(object sender, EventArgs e)

    if (Request.HttpMethod == "OPTIONS")
    
        Response.End();
    

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

(除了添加标题)

【讨论】:

以上是关于如何使用 WCF 自托管处理 Ajax JQUERY POST 请求的主要内容,如果未能解决你的问题,请参考以下文章

自托管 WCF 服务:如何从托管应用程序访问实现服务合同的对象?

在 WCF 自托管服务中指定 Singleton 服务

WCF 错误处理

如何实现自托管 WCF 数据服务(http://localhost:1234/myDataService.svc/...)

如何让我的自托管 WCF 服务不尝试保留 localhost 以外的 url?

我可以在我的自托管 WCF 应用程序中使用 Let's Encrypt 证书吗?