2011年12月9日

[WCF]Invoke <enableWebScript> WCF Service

有一個已經在production上的WCF Service, 它的設定檔如下

<services>
<service behaviorConfiguration="defaultServiceBehavior" name="Trend.BPM.AppWeb.Services.AjaxPipe">
<endpoint address="" behaviorConfiguration="Trend.BPM.AppWeb.Services.AjaxPipeAspNetAjaxBehavior"
binding="webHttpBinding" bindingConfiguration="webHttpBinding"
name="ajaxPipeEndpoint" contract="Trend.BPM.AppWeb.Services.AjaxPipe">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</service>
</services>
<bindings>
<webHttpBinding>
<binding name="webHttpBinding" allowCookies="true" bypassProxyOnLocal="true"
maxBufferPoolSize="524288000" maxReceivedMessageSize="65536000">
<readerQuotas maxDepth="32" maxStringContentLength="8192000"
maxArrayLength="16384000" maxBytesPerRead="409600000" maxNameTableCharCount="16384000" />
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Windows" />
</security>
</binding>
</webHttpBinding>
</bindings>



<behaviors>
<endpointBehaviors>
<behavior name="Trend.BPM.AppWeb.Services.AjaxPipeAspNetAjaxBehavior">
<enableWebScript />
</behavior>
</endpointBehaviors>
</behaviors>



程式碼



[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
public class AjaxPipe {
[OperationContract]
public string ProcessForm(string type, string action, string content, string workItemData, string clientTimeZone) {
//...
}


這個service原本主要是當成jQuery AJAX的呼叫使用, 但是有一個狀況需要在後端使用C#呼叫.


原本我用非常直觀的方式, 產生一個proxy, 並使用ChannelFactory呼叫.


//Create Binding
protected BasicHttpBinding CreateDefaultBinding(BasicHttpSecurityMode securityMode) {
BasicHttpBinding binding = new BasicHttpBinding(securityMode);
binding.Security.Transport = new HttpTransportSecurity {
ClientCredentialType = HttpClientCredentialType.Windows
};

return binding;
}

//Create Channel
protected T GetServiceChannel<T>(string configurationSection, string url) {
BasicHttpBinding binding = CreateDefaultBinding();
EndpointAddress endpoint = null;
if (string.IsNullOrEmpty(url)) {
endpoint = new EndpointAddress(GetServiceURL(configurationSection));
}
else {
endpoint = new EndpointAddress(url);
}
return new ChannelFactory<T>(binding, endpoint).CreateChannel();
}

//Invoke
var svc = GetServiceChannel<AJAX.AjaxPipe>(config,url);
svc.ProcessForm("Test","Submit",null,null,null);



顯然這個方式會失敗, 因為Server是使用WebHttpBinding, 而不是BasicHttpBinging, 因此我修改我的程式為使用WebHTtpBinging


public AJAXPIPE.AjaxPipe GetWebAJAXPipe(string url) {

if (string.IsNullOrEmpty(url))
throw new ArgumentNullException("URL");

var binding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
ChannelFactory<AJAXPIPE.AjaxPipe> cf = new ChannelFactory<AJAXPIPE.AjaxPipe>(binding, url);
var behavior = new WebHttpBehavior();
cf.Endpoint.Behaviors.Add(behavior);
return cf.CreateChannel();
}


看起來已經使用了WebHttpBinding, Secutiry Mode也都符合了Server設定, 但是呼叫時會得到這個錯誤


Operation 'ProcessForm' of contract 'AjaxPipe' specifies multiple request

body parameters to be serialized without any wrapper elements.


At most one body parameter can be serialized without wrapper elements.


Either remove the extra body parameters or set the BodyStyle property


on the WebGetAttribute/WebInvokeAttribute to Wrapped.



Google的結果, 大多是說要修改Server的config, 使用<webHttp>而不是使用<enableWebScript>, 但是因為這個Service已經在production, 修改這裡可能需要一些測試驗證, 因此我比較想要在不更改Server的狀況下達到呼叫的目的.



於是, 將程式碼修改如下



public AJAXPIPE.AjaxPipe GetWebAJAXPipe(string url) {
if (string.IsNullOrEmpty(url))
throw new ArgumentNullException("URL");
var binding = new WebHttpBinding(WebHttpSecurityMode.TransportCredentialOnly);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
ChannelFactory<AJAXPIPE.AjaxPipe> cf = new ChannelFactory<AJAXPIPE.AjaxPipe>(binding, url);
var behavior = new WebHttpBehavior() {
DefaultBodyStyle = WebMessageBodyStyle.Wrapped,
FaultExceptionEnabled = true, //for debug
HelpEnabled = true //for debug
};

cf.Endpoint.Behaviors.Add(behavior);
return cf.CreateChannel();
}



雖然結果只有一行, 但是我只想說…終於打完收工了…

About Me