自托管 WCF REST 服务 JSON POST 方法不允许

Posted

技术标签:

【中文标题】自托管 WCF REST 服务 JSON POST 方法不允许【英文标题】:Self Hosted WCF REST service JSON POST Method Not Allowed 【发布时间】:2015-11-20 14:23:21 【问题描述】:

到目前为止,我已经在互联网上看到了数千个这样的“WCF”问题,但我开始认为这是不可能的。请有人告诉我我错了...

背景:我正在使用自托管 WCF 服务(因此 Global.asax.cs 在这里没有帮助)。端点也是以编程方式定义的。合同用 WebInvoke(Method="POST") 装饰,我正在对服务进行 JQuery 调用。

预检最初使用 OPTIONS 方法工作,但 POST 方法失败并出现 405 Method Not Allowed。 GET 函数也可以完美运行。

我已经在互联网上搜索并尝试了大约一个月,但它就是不会让步。该服务已经对另一个通过 TCP 调用它的客户端做出了很好的响应......请一些天才帮助我。谢谢

PS:我认为 POST 响应真的很奇怪,是 Allow: OPTIONS... 肯定不应该存在吗?

CORS

    public class CORSEnablingBehavior : BehaviorExtensionElement, IEndpointBehavior
    
    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
    
        var requiredHeaders = new Dictionary<string, string>();

        requiredHeaders.Add("Access-Control-Allow-Origin", "*");
        requiredHeaders.Add("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS");
        requiredHeaders.Add("Access-Control-Allow-Headers", "Origin, Cache-Control, Connection, Pragma, Content-Length, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, User-Agent");

        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new CORSHeaderInjectingMessageInspector(requiredHeaders));
    

app.config

  <system.serviceModel>
<behaviors>
  <endpointBehaviors>
    <behavior name="SOAPDemoEndpointBehavior">
    </behavior>
    <behavior>
      <webHttp/>
      <crossOriginResourceSharingBehavior/>
    </behavior>
  </endpointBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
<extensions>
  <behaviorExtensions>
    <add name="crossOriginResourceSharingBehavior" type="Application.Host.CORSEnablingBehavior, Application.Host, Version=1.0.0.0, Culture=neutral"/>
  </behaviorExtensions>
</extensions>
<bindings>
  <basicHttpBinding>
    <binding name="OrdersMappingSoap"/>
  </basicHttpBinding>

  <!--2015-08-26-->
  <webHttpBinding>
    <binding name="webHttpBindingWithJson"
          crossDomainScriptAccessEnabled="true" />
  </webHttpBinding>

界面

[OperationContract(Name = "Relational")] 
[FaultContract(typeof(ValidationFault))]
[WebInvoke(Method = "POST", UriTemplate = "GetCustomerRelational", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Wrapped)]
CustomerFullModel GetCustomerRelational(int clientHandle, object customerID, bool loadRelationalData);

JQuery

jQuery.ajax(
  crossDomain: true,
  type: "POST",
  contentType: "application/json",
  url: "http://localhost:8086/CustomerService/rest/GetCustomerRelational/",
  data: JSON.stringify(
    "clientHandle": 1824,
    "customerID": "ABB029",
    "loadRelationalData": true
  ),
  dataType: "json",
  success: function(result) 
    console.log("Success...");
    document.getElementById("lblResponse").innerhtml = "Success: " + JSON.stringify(result.NormalResult);
  ,
  error: function(x, s, t) 
    console.log("Error...");
    document.getElementById("lblResponse").innerHTML = x.responseText;
  
);
&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;

预检请求

OPTIONS http://localhost:8086/CustomerService/rest/GetCustomerRelational/ HTTP/1.1
Host: localhost:8086
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: null
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36
Access-Control-Request-Headers: accept, content-type
Accept: */*
Referer: http://stacksnippets.net/js
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8

预检响应

HTTP/1.1 200 OK
Content-Length: 0
Server: Microsoft-HTTPAPI/2.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Origin, Cache-Control, Connection, Pragma, Content-Length, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, User-Agent
Date: Wed, 26 Aug 2015 13:13:59 GMT

POST 请求

POST http://localhost:8086/CustomerService/rest/GetCustomerRelational/ HTTP/1.1
Host: localhost:8086
Connection: keep-alive
Content-Length: 69
Accept: application/json, text/javascript, */*; q=0.01
Origin: null
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36
Content-Type: application/json
Referer: http://stacksnippets.net/js
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.8
"clientHandle":1824,"customerID":"ABB029","loadRelationalData":true

POST 响应

HTTP/1.1 405 Method Not Allowed
Allow: OPTIONS
Content-Length: 1565
Content-Type: text/html; charset=UTF-8
Server: Microsoft-HTTPAPI/2.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Origin, Cache-Control, Connection, Pragma, Content-Length, Content-Type, Accept, Accept-Encoding, Accept-Language, Host, User-Agent
Date: Wed, 26 Aug 2015 13:14:02 GMT

<p>Method not allowed.</p>

【问题讨论】:

据我记忆,jsonp(crossDomainScriptAccessEnabled 选项) 不适用于POST 如果有人问,我已经按照以下教程进行操作link、link 和link 谢谢 Eser,我太绝望了,我还是尝试了... 【参考方案1】:

我想通了。

这个 WCF 与其他所有人的主要区别在于,我的 WCF 是自托管的,而其他一切都托管在 IIS 上(大部分)。

感谢这篇文章ASP.NET Compatibility Mode,答案就在于预检请求的拦截。 IIS 托管的 WCF 要求在 global.asax 文件中进行拦截,如下所示:

protected void Application_BeginRequest(object sender, EventArgs e)
    
        if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
        
            //These headers are handling the "pre-flight" OPTIONS call sent by the browser
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE,OPTIONS");
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "X-Requested-With,Content-Type");
            HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "*");
            HttpContext.Current.Response.End();
        
    

但是,这在自托管 WCF 中是不可能的。但是,我们仍然可以使用 app.config 中的这一行来使用 ASP.NET 功能

<serviceHostingEnvironment aspNetCompatibilityEnabled="true" />

最后在服务类中使用这个:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class TestService : ValidationModel, ITestService

我意识到将其设置为“允许”并没有帮助,它必须是“必需”。

Final finally,为了开始预检,接口和服务中需要以下代码:

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

public void GetOptions()
    
        WebOperationContext.Current.OutgoingResponse.StatusCode = System.Net.HttpStatusCode.OK;
    

现在,带有复杂参数的 POST 方法将通过预检测试并执行带有响应的方法。

【讨论】:

This however, is not possible in self hosted WCF -->>> WebOperationContext.Current.OutgoingResponse.Headers.Add 刚试过这个,设置RequirementsMode = AspNetCompatibilityRequirementsMode.Required 在启动我的服务时返回了一个错误,说明它需要托管在IIS 中或具有Required 以外的设置。 工作......终于经过 3 天和无休止的阅读、搜索和尝试。如果可以的话,我会给你很多赏金,THX!

以上是关于自托管 WCF REST 服务 JSON POST 方法不允许的主要内容,如果未能解决你的问题,请参考以下文章

通过 HTTPS 对 WCF 自托管 REST 服务的 POST 请求示例

WCF Rest 自托管证书安全服务返回 401 未经授权

在 WCF REST 服务 POST 方法中处理 Json 请求数据

将 JSON 发送到 WCF Rest 服务 - 对象始终为空

WCF Rest POST 方法接受 JSON 和 XML

如何使用 wcf REST 服务获取自定义对象的 json 响应?