Endpoint behavior configuration WCF with Protobuf-net - wcf

I have a WCF service (.NET 4) that exposes 4 endpoints of which one endpoint is configured with protobuf-net (V1.0.0.280) behavior extension. However, I noticed that protobuf-net behavior kicks in for ALL defined endpoints including the ones protbuf-net is not configured for! I have pasted my config below. Am I missing something? Any help is greatly appreciated .. thx
<service name="MyService" behaviorConfiguration="MyServiceBehavior">
<endpoint address="Http.Basic" binding="basicHttpBinding" bindingConfiguration="Http.Basic.Config" contract="IMyService" behaviorConfiguration="DefaultBehavior" />
<endpoint address="Http.Binary" binding="customBinding" bindingConfiguration="Http.Binary.Config" contract="IMyService" behaviorConfiguration="DefaultBehavior" />
<endpoint address="Tcp.Binary" binding="customBinding" bindingConfiguration="Tcp.Binary.Config" contract="IMyService" behaviorConfiguration="DefaultBehavior" />
<endpoint address="Http.ProtoBuf" binding="basicHttpBinding" bindingConfiguration="Http.Basic.Config" contract="IMyService" behaviorConfiguration="ProtoBufBehavior" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8085/MyService"/>
<add baseAddress="net.tcp://localhost:8086/MyService"/>
</baseAddresses>
</host>
</service>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
<endpointBehaviors>
<behavior name="DefaultBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
<behavior name="ProtoBufBehavior">
<ProtoBufSerialization />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="Http.Basic.Config" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" />
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
<customBinding>
<binding name="Http.Binary.Config" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00">
<binaryMessageEncoding />
<httpTransport allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" />
</binding>
<binding name="Tcp.Binary.Config" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00">
<binaryMessageEncoding />
<tcpTransport hostNameComparisonMode="StrongWildcard" />
</binding>
</customBinding>
</bindings>

That is odd, but (checks the code) I only ever apply changes within an endpoint supplied to me by WCF:
void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
ReplaceDataContractSerializerOperationBehavior(endpoint);
}
void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
ReplaceDataContractSerializerOperationBehavior(endpoint);
}
private static void ReplaceDataContractSerializerOperationBehavior(ServiceEndpoint serviceEndpoint)
{
foreach (OperationDescription operationDescription in serviceEndpoint.Contract.Operations)
{
ReplaceDataContractSerializerOperationBehavior(operationDescription);
}
}
private static void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
{
DataContractSerializerOperationBehavior dcsOperationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();
if (dcsOperationBehavior != null)
{
description.Behaviors.Remove(dcsOperationBehavior);
description.Behaviors.Add(new ProtoOperationBehavior(description));
}
}
i.e. "given an endpoint (by WCF), loop over each operation (method) in that endpoint, and change the serializer from DCS to PB"
This raises the intriguing possibility that the contract definitions (and hence the operation definitions) are themselves shared between all the endpoints - but I am honestly not sure about that. If that is the case, I don't see that it would ever be possible to have different processors per endpoint. I am not, however, a WCF-guru. This is... perplexing.

Related

Creating REST and SOAP endpoints for a WCF service?

I need a service with two endpoints.When I create service with 1 endpoint point, it works fine but when I add 2 endpoints it complains endpoint not found.
Endpoint without address extension works fine but not with address ('download')
Here is my configuration:
Server
<serviceBehaviors>
<behavior name="MessageStreamBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true" httpHelpPageEnabled="true" />
<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<!--stream for video upload-->
<binding name="httpLargeMessageStream"
maxReceivedMessageSize="4294967295"
openTimeout="00:01:00" closeTimeout="00:30:00" sendTimeout="00:30:00"
transferMode="Streamed">
<security mode="Transport" />
</binding>
</basicHttpBinding>
<webHttpBinding>
<!--stream for video download-->
<binding name="webHttpLargeMessageStream"
maxReceivedMessageSize="4294967295"
openTimeout="00:01:00" closeTimeout="00:30:00" sendTimeout="00:30:00"
transferMode="Streamed">
<security mode="Transport" />
</binding>
</webHttpBinding>
</bindings>
<services>
<!--service for streamservice; video upload and download-->
<service name="Trigger.CSSD.Service.Services.MediaService" behaviorConfiguration="MessageStreamBehavior" >
<endpoint name="restDownloadHttpStream"
behaviorConfiguration="webHttpBehavior" address="download"
binding="webHttpBinding" bindingConfiguration="webHttpLargeMessageStream"
contract="Trigger.CSSD.Service.Services.IDownloadMediaService" />
<endpoint name="uploadHttpStream"
binding="basicHttpBinding" bindingConfiguration="httpLargeMessageStream"
contract="Trigger.CSSD.Service.Services.IUploadMediaService" />
</service>
</services>
Client
<endpoint address="https://cssd-services:44300/MediaService.svc"
binding="basicHttpBinding" bindingConfiguration="basicHttpStream"
contract="MediaService.IUploadMediaService" name="basicHttpStream" />
<endpoint address="https://cssd-services:44300/MediaService.svc/download"
binding="webHttpBinding" bindingConfiguration="webHttpStream" behaviorConfiguration="webHttpBehavior"
contract="MediaService.IDownloadMediaService" name="webHttpStream" />
</client>
SERVICE
[ServiceContract]
public interface IDownloadMediaService
{
[OperationContract]
// [ContentType("video/mp4")]
// [WebGet(UriTemplate = "media/{name}")]
Stream GetMedia(String name);
}
[ServiceContract]
public interface IUploadMediaService
{
//http://msdn.microsoft.com/en-us/library/ms751463.aspx
[OperationContract]
FileUploadResponseMessage UploadFile(FileUploadMessage message);
}
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class MediaService : IDownloadMediaService, IUploadMediaService
{......
}
ANy suggestion? Thanks.

Duplication of EndPoint but i cannot see it

I have a web service.
This is (part of my) my web config:
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="LicensedBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
<behavior name="NetTCPBehaviour">
<serviceTimeouts transactionTimeout="0.00:00:30" />
<serviceDebug includeExceptionDetailInFaults="false" />
<dataContractSerializer maxItemsInObjectGraph="65536" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service name="License" behaviorConfiguration="LicensedBehaviour">
<endpoint address="License.svc" binding="basicHttpBinding" bindingConfiguration="NormalHttpBindingEndPoint" contract="ILicense" name="wsLicense" />
</service>
<service name="testme" behaviorConfiguration="NetTCPBehaviour">
<endpoint address="net.tcp://localhost:808/Sync2.svc" binding="netTcpBinding" contract="ISync2" name="wsMotionUploader" bindingConfiguration="NetTCPBindingEndPoint"/>
</service>
</services>
<bindings>
<basicHttpBinding>
<binding name="NormalHttpBindingEndPoint" closeTimeout="00:02:00" openTimeout="00:02:00">
<readerQuotas maxArrayLength="32768" maxStringContentLength="2147483647" />
</binding>
</basicHttpBinding>
<netTcpBinding>
<binding name="NetTCPBindingEndPoint" receiveTimeout="00:15:00" sendTimeout="00:15:00" transferMode="Streamed" closeTimeout="00:02:00" openTimeout="00:02:00"
maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" maxBufferSize="2147483647">
<readerQuotas maxArrayLength="32768" />
<security mode="None">
<transport clientCredentialType="None" protectionLevel="None" />
<message clientCredentialType="None" />
</security>
</binding>
</netTcpBinding>
</bindings>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
</system.serviceModel>
This is my service:
public void DoWork(Stream image)
{
var img = System.Drawing.Bitmap.FromStream(image);
}
This is my interface:
[OperationContract(IsOneWay = true)]
void DoWork(Stream image);
This is my client:
wsSyncFastest.Sync2Client client = new wsSyncFastest.Sync2Client();
using (Bitmap bmp = new Bitmap("d:\\bf.jpg"))
{
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
client.DoWork(ms);
ms.Close();
}
}
This is my error message:
An endpoint configuration section for contract 'wsSyncFastest.ISync2' could not be loaded because more than one endpoint configuration for that contract was found. Please indicate the preferred endpoint configuration section by name.
this is my stack Error:
at System.ServiceModel.Description.ConfigLoader.LookupChannel(ContextInformation configurationContext, String configurationName, ContractDescription contract, EndpointAddress address, Boolean wildcard, Boolean useChannelElementKind, ServiceEndpoint& serviceEndpoint)
at System.ServiceModel.Description.ConfigLoader.LookupEndpoint(String configurationName, EndpointAddress address, ContractDescription contract, ContextInformation configurationContext)
at System.ServiceModel.Description.ConfigLoader.LookupEndpoint(String configurationName, EndpointAddress address, ContractDescription contract)
at System.ServiceModel.ChannelFactory.InitializeEndpoint(String configurationName, EndpointAddress address)
at System.ServiceModel.ChannelFactory1..ctor(String endpointConfigurationName, EndpointAddress remoteAddress)
at System.ServiceModel.ChannelFactory1..ctor(String endpointConfigurationName)
at System.ServiceModel.ConfigurationEndpointTrait1.CreateSimplexFactory()
at System.ServiceModel.ConfigurationEndpointTrait1.CreateChannelFactory()
at System.ServiceModel.ClientBase1.CreateChannelFactoryRef(EndpointTrait1 endpointTrait)
at System.ServiceModel.ClientBase1.InitializeChannelFactoryRef()
at System.ServiceModel.ClientBase1..ctor()
at LiteEdition.wsSyncFastest.Sync2Client..ctor() in g:\dev20140604\LiteEdition\LiteEdition\Service References\wsSyncFastest\Reference.cs:line 51
at LiteEdition.StartUp.button1_Click(Object sender, EventArgs e) in g:\dev20140604\LiteEdition\LiteEdition\StartUp.cs:line 2646
I cannot see any probelms but obviously there is.
Can anyone assist?
ADDITIONAL:
My client app.config:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="wsLicense" />
<binding name="BasicHttpBinding_ISync2" />
</basicHttpBinding>
<netTcpBinding>
<binding name="NetTcpBinding_ISync2" />
</netTcpBinding>
</bindings>
<client>
<endpoint address="http://aurl/License.svc/License.svc"
binding="basicHttpBinding" bindingConfiguration="wsLicense"
contract="wsLicense.ILicense" name="wsLicense" />
<endpoint address="http://www.informedmotion.co.uk/Sync2.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISync2"
contract="wsSyncFastest.ISync2" name="BasicHttpBinding_ISync2" />
<endpoint address="net.tcp://dsvr019492/Sync2.svc" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_ISync2" contract="wsSyncFastest.ISync2"
name="NetTcpBinding_ISync2">
<identity>
<servicePrincipalName value="host/DSVR019492" />
</identity>
</endpoint>
</client>
</system.serviceModel>
Try passing the name of the endpoint in when you create the proxy object and see if that fixes it.
If you intend on using the basicHttpBinding, it will be:
wsSyncFastest.Sync2Client client = new wsSyncFastest.Sync2Client("BasicHttpBinding_ISync2");
If you intend on using the netTcpBinding it will be:
wsSyncFastest.Sync2Client client = new wsSyncFastest.Sync2Client("NetTcpBinding_ISync2");

WCF Session Configuration error

i want to enable (SessionMode=SessionMode.Required) in my service, so when i have enabled it then test the service using WCF Client Test it raise the following error:
The message could not be processed. This is most likely because the action 'http://schemas.xmlsoap.org/ws/2004/09/transfer/Get' is incorrect or because the message contains an invalid or expired security context token or because there is a mismatch between bindings. The security context token would be invalid if the service aborted the channel due to inactivity. To prevent the service from aborting idle sessions prematurely increase the Receive timeout on the service endpoint's binding.HTTP GET Error
URI: http://localhost:7645/PublisherService.svc
The HTML document does not contain Web service discovery information.
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="myBasicBinding" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" closeTimeout="00:01:00" openTimeout="00:01:00" sendTimeout="00:01:00" receiveTimeout="00:01:00">
<readerQuotas maxDepth="128" maxStringContentLength="8388608" maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="2147483647"/>
</binding>
</basicHttpBinding>
<!--enable WSHTTPBinding session-->
<wsHttpBinding>
<binding name="bindingAction" transactionFlow="false" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" sendTimeout="00:01:00" receiveTimeout="00:01:00" closeTimeout="00:01:00" openTimeout="00:01:00">
<readerQuotas maxDepth="128" maxStringContentLength="8388608" maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="2147483647"/>
<reliableSession enabled="true"/>
<security mode="Transport">
<message establishSecurityContext="false" clientCredentialType="IssuedToken"/>
</security>
</binding>
</wsHttpBinding>
</bindings>
<protocolMapping>
<add scheme="http" binding="wsHttpBinding"/>
</protocolMapping>
<!-- \\\\\\\\\\\\\\\\\\\\\\\\\\\ -->
<services>
<service name="AllChatService.PublisherService" behaviorConfiguration="metadataSupport">
<host>
<baseAddresses>
<add baseAddress ="http://localhost:7645/"/>
</baseAddresses>
</host>
<endpoint contract="AllChatService.PublisherService" binding="wsHttpBinding" address=""/>
<!--Enable Meta Data Publishing-->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="metadataSupport">
<serviceDebug includeExceptionDetailInFaults="False" />
<!--Enable WSDL Data Binding-->
<serviceMetadata httpGetEnabled="true" httpGetUrl=""/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
</system.serviceModel>
[ServiceBehaviorAttribute(InstanceContextMode = InstanceContextMode.PerCall)]
public class PublisherService : IPublisher
{
}
[ServiceContract(SessionMode = SessionMode.Required)]
public interface IPublisher
{
}
So can any one help me to solve this problem.
remove the line
<security mode="Transport">
from your web.config file. because Transport seems to require HTTPS to encrypt credentials.

Problem calling WCF service internet

I am developing a WCF service that will be called by customer in internet. The service is hosted in IIS7 and accept only http. For clients call us from https we do is have a reverse proxy that forwards the request to the application https to http. The customer give a https url to connect and does so smoothly, adding the reference to the service properly. The problem comes when trying to create a client and add in your endpoint https and execute it, as it reads:
System.ArgumentException: The provided URI scheme 'https' is invalid,
expected 'http'. Parameter name: via.
I leave part of the service's web.config:
<bindings>
<wsHttpBinding>
<binding name="ConfigEP">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true">
<baseAddressPrefixFilters>
<add prefix="http://serverInterno/App/"/>
</baseAddressPrefixFilters>
</serviceHostingEnvironment>
<services>
<service behaviorConfiguration="App.AppM_NameBehavior" name="App.AppM_Name">
<endpoint address="" behaviorConfiguration="App.AppM_NameEPBehavior" binding="wsHttpBinding" bindingConfiguration="ConfigEP" name="App.AppM_NameEP" bindingNamespace="http://siteName/AppM_Name" contract="App.IAppM_Name" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="App.AppM_NameEPBehavior">
<wsdlExtensions location="https://urlsegura/App/Appm_Name.svc" singleFile="true" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="App.AppM_NameBehavior">
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="false" />
<serviceCredentials>
<clientCertificate>
<authentication customCertificateValidatorType="App.Validador, App" certificateValidationMode="Custom" />
</clientCertificate>
<serviceCertificate findValue="XX XX XX XX XX XX XX XX XX XX" x509FindType="FindBySerialNumber" />
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="wsdlExtensions" type="WCFExtras.Wsdl.WsdlExtensionsConfig, WCFExtras, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</behaviorExtensions>
</extensions>
and here the client's app.config:
<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="NewBehavior">
<clientCredentials>
<clientCertificate findValue="XX XX XX XX XX XX XX XX XX XX" x509FindType="FindBySerialNumber" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="App.AppM_NameEP" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="Message">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="Certificate" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://urlsegura/App/Appm_Name.svc" binding="wsHttpBinding" bindingConfiguration="App.AppM_NameEP" contract="App.IAppM_Name" name="App.AppM_NameEP">
<identity>
<certificate encodedValue="XXXX" />
</identity>
</endpoint>
</client>
</system.serviceModel>
Thanks in advance.
Best regards.
I think your error is being caused because you're using message based security on your configuration. Try changing it to Transport instead (in both the client and service configuration files), so that it uses SSL for security rather than encrypting the message.
You can use TransportWithMessageCredential if you absolutely must have the message encrypted also. Hope that helps.
I don't understand the reverse proxy you describe but it seems you're trying to support access from both HTTP & HTTPS. To do this, you will need to add a second endpoint. You'd configure the service something like this:
<wsHttpBinding>
<binding name="ConfigEP">
<security mode="Message">
<message clientCredentialType="Certificate" />
</security>
</binding>
<binding name="ConfigEPHttps">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
and this add the new endpoint:
<service behaviorConfiguration="App.AppM_NameBehavior" name="App.AppM_Name">
<endpoint address="" behaviorConfiguration="App.AppM_NameEPBehavior"
binding="wsHttpBinding"
bindingConfiguration="ConfigEP"
name="App.AppM_NameEP"
bindingNamespace="http://siteName/AppM_Name"
contract="App.IAppM_Name" />
<endpoint address="secure" behaviorConfiguration="App.AppM_NameEPBehavior"
binding="wsHttpBinding"
bindingConfiguration="ConfigEPHttps"
name="App.AppM_NameEPHttps"
bindingNamespace="http://siteName/AppM_Name"
contract="App.IAppM_Name" />
</service>
You also need make this change to get the WSDL over HTTPS:
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />

SAP authentication with WCF Adapter Service project

I've been experimenting with the BizTalk Adapter Pack, specifically with the SAP stuff.
I'm able to connect to SAP and run an RFC from a client app (e.g. Console, Windows, Web site) using the SAP binding directly, no problem at all. I've also got it all working in BizTalk Server 2009 using orchestration, send port, receive port, etc.
However, I want to expose SAP functionality to internal users as a standard HTTP web service, without requiring them to have the SAP binding stuff installed on their PC.
So, I created a "WCF Adapter Service" project in Visual Studio, and followed the wizard. I then created a standard web app for the client end, and added a proxy using "Add service reference". It all worked OK, in that it found the service and added the proxy code, but when I try to invoke the service I get the error SapErrorMessage=Incomplete logon data.
What I don't know how to do is pass SAP credentials from my client web app to the Basic HTTP service and then on the the SAP binding. If I put the SAP credentials into the SAP connection string it all works OK, but this is not very secure and also I want to supply the credentials from the client (i.e. ask the user to provide their SAP credentials).
In some examples I've seen, e.g. http://msdn.microsoft.com/en-us/library/dd788594(BTS.10).aspx, the SAP credentials are passed in HTTP headers. Unfortunately, all the examples I've seen of this go on to show how to invoke the service from SharePoint, where there's a dialog window for setting the headers. I'm not using SharePoint! I've tried adding a "" section to my client endpoint configuration, but this didn't seem to work.
So, what is the recommended way to pass SAP credentials to a Basic HTTP web service created via the "WCF Adapter Service" wizard?
For information, this is the relevant part of the web.config on my client web app:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="RfcEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
<message clientCredentialType="UserName" algorithmSuite="Default"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://xxxxxx/SAP_Service_1/Rfc.svc" binding="basicHttpBinding" bindingConfiguration="RfcEndpoint" contract="ServiceReference1.Rfc" name="RfcEndpoint">
<headers>
<SAP_Username>username</SAP_Username>
<SAP_Password>password</SAP_Password>
</headers>
</endpoint>
</client>
</system.serviceModel>
And this is the web.config generated by the WCF Adapter Service project wizard:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service behaviorConfiguration="customServiceBehavior" name="RfcClient">
<endpoint address="" behaviorConfiguration="customEndpointBehavior" binding="basicHttpBinding" bindingConfiguration="RfcClientBindingConfig" name="RfcEndpoint" contract="Rfc"/>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="customEndpointBehavior">
<endpointBehavior usernameHttpHeader="SAP_Username" passwordHttpHeader="SAP_Password" adapterSecurityBridgeType="HTTPUsernamePassword"/>
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="customServiceBehavior">
<serviceMetadata httpsGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true"/>
<serviceCredentials type="Microsoft.ServiceModel.Channels.AdapterServiceCredentials, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Channels.AdapterServiceAuthorizationManager, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<authorizationPolicies>
<add policyType="Microsoft.ServiceModel.Channels.AdapterAuthorizationPolicy, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</authorizationPolicies>
</serviceAuthorization>
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="endpointBehavior" type="Microsoft.ServiceModel.Channels.AdapterEndpointBehavior, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</behaviorExtensions>
</extensions>
<bindings>
<basicHttpBinding>
<binding name="RfcClientBindingConfig">
<security mode="None">
<transport clientCredentialType="None"/>
<message clientCredentialType="UserName"/>
</security>
</binding>
</basicHttpBinding>
<sapBinding>
<binding name="SAPBinding" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" enableBizTalkCompatibilityMode="false" receiveIdocFormat="Typed" enableSafeTyping="false" generateFlatFileCompatibleIdocSchema="true" maxConnectionsPerSystem="50" enableConnectionPooling="true" idleConnectionTimeout="00:15:00" flatFileSegmentIndicator="SegmentDefinition" enablePerformanceCounters="false" autoConfirmSentIdocs="false" acceptCredentialsInUri="true" padReceivedIdocWithSpaces="false" sncLibrary="" sncPartnerName="" rfcAllowStartProgram="">
<dataTypesBehavior datsMinToDateTime="NULL" datsMaxToDateTime="NULL" invalidDatsToDateTime="ERROR" emptyDatsToDateTime="0001-01-01T00:00:00" emptyTimsToDateTime="0001-01-01T00:00:00" dateTimeMaxToDats="99991231" dateTimeMinToDats="00010101" timsMaxToDateTime="NULL" invalidTimsToDateTime="ERROR" dateTimeMaxToTims="235959" dateTimeMinToTims="000000" invalidNumcToInt="0" emptyNumcToInt="0" dateTimeNullToDats="SKIP" dateTimeNullToTims="SKIP"/>
</binding>
</sapBinding>
</bindings>
<client>
<endpoint address="sap://CLIENT=300;LANG=EN;#a/XXXXXX/00?RfcSdkTrace=False&AbapDebug=False" binding="sapBinding" bindingConfiguration="SAPBinding" contract="Rfc" name="SAPBinding_Rfc"/>
</client>
</system.serviceModel>
</configuration>
I'm a bit new to WCF, so any help or pointers gratefully received!
Thanks
Doug
Got it working at last!
First, I needed to get the SAP username and password added as HTTP headers in the request. I tried the simple solution of editing the config file first, as some have suggested:
<endpoint ....
<headers>
<HeaderName1>Header Value 1</HeaderName1>
<HeaderName2>Header Value 2</HeaderName2>
</headers>
</endpoint>
But this does not add HTTP headers, or at least I wasn't able to get it to work.
I looked at the excellent article here http://ericphan.info/blog/2010/6/3/adding-custom-http-header-to-all-wcf-requests.html, which explains how to user a MessageInspector to add HTTP headers to the outgoing request. It works brilliantly, but headers are defined in the config file. I needed a way to set the headers in code. Perhaps there would be a way of adapting this code, but I'm not that clever!
Instead, I found some other examples and distilled it down to this:
using (RfcClient client = new RfcClient("RfcEndpoint"))
{
try
{
using (OperationContextScope scope = new OperationContextScope(client.InnerChannel))
{
HttpRequestMessageProperty httpRequestProperty;
if (OperationContext.Current.OutgoingMessageProperties.ContainsKey(HttpRequestMessageProperty.Name))
{
httpRequestProperty = (HttpRequestMessageProperty)OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name];
}
else
{
httpRequestProperty = new HttpRequestMessageProperty();
}
httpRequestProperty.Headers.Add("SAP_Username", "dXNlcm5hbWU=");
httpRequestProperty.Headers.Add("SAP_Password", "cGFzc3dvcmQ=");
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = httpRequestProperty;
ret = client.BAPI_SALESORDER_GETLIST(cust, null, null, null, po, null, salesOrg, transGroup, ref orders);
GridView1.DataSource = orders;
GridView1.DataBind();
}
client.Close();
Label1.Text = DateTime.Now.ToString();
}
catch (CommunicationException ex)
{
StringBuilder sb = new StringBuilder();
sb.Append(ex.Message);
if (ex.InnerException != null) sb.Append("~" + ex.InnerException.Message);
Label1.Text = sb.ToString();
client.Abort();
}
catch (TimeoutException ex)
{
Label1.Text = ex.Message;
client.Abort();
}
catch (Exception ex)
{
Label1.Text = ex.Message;
client.Abort();
throw;
}
}
For completeness, this is the web.config file for the service:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<services>
<service behaviorConfiguration="customServiceBehavior" name="RfcClient">
<endpoint address="" behaviorConfiguration="customEndpointBehavior"
binding="basicHttpBinding" bindingConfiguration="RfcClientBindingConfig"
name="RfcEndpoint" contract="Rfc" />
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="customEndpointBehavior">
<endpointBehavior usernameHttpHeader="SAP_Username" passwordHttpHeader="SAP_Password"
adapterSecurityBridgeType="HTTPUsernamePassword" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="customServiceBehavior">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials type="Microsoft.ServiceModel.Channels.AdapterServiceCredentials, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<serviceCertificate findValue="servername" storeLocation="LocalMachine"
storeName="My" x509FindType="FindBySubjectName" />
</serviceCredentials>
<serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Channels.AdapterServiceAuthorizationManager, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<authorizationPolicies>
<add policyType="Microsoft.ServiceModel.Channels.AdapterAuthorizationPolicy, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</authorizationPolicies>
</serviceAuthorization>
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="endpointBehavior" type="Microsoft.ServiceModel.Channels.AdapterEndpointBehavior, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</behaviorExtensions>
</extensions>
<bindings>
<basicHttpBinding>
<binding name="RfcClientBindingConfig">
<security mode="Transport">
<transport clientCredentialType="None" />
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
<sapBinding>
<binding name="SAPBinding" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" enableBizTalkCompatibilityMode="false"
receiveIdocFormat="Typed" enableSafeTyping="false" generateFlatFileCompatibleIdocSchema="true"
maxConnectionsPerSystem="50" enableConnectionPooling="true"
idleConnectionTimeout="00:15:00" flatFileSegmentIndicator="SegmentDefinition"
enablePerformanceCounters="false" autoConfirmSentIdocs="false"
acceptCredentialsInUri="false" padReceivedIdocWithSpaces="false"
sncLibrary="" sncPartnerName="" rfcAllowStartProgram="">
<dataTypesBehavior datsMinToDateTime="NULL" datsMaxToDateTime="NULL"
invalidDatsToDateTime="ERROR" emptyDatsToDateTime="0001-01-01T00:00:00"
emptyTimsToDateTime="0001-01-01T00:00:00" dateTimeMaxToDats="99991231"
dateTimeMinToDats="00010101" timsMaxToDateTime="NULL" invalidTimsToDateTime="ERROR"
dateTimeMaxToTims="235959" dateTimeMinToTims="000000" invalidNumcToInt="0"
emptyNumcToInt="0" dateTimeNullToDats="SKIP" dateTimeNullToTims="SKIP" />
</binding>
</sapBinding>
</bindings>
<client>
<endpoint address="sap://CLIENT=300;LANG=EN;#a/XXXXXX/00?RfcSdkTrace=False&AbapDebug=False"
binding="sapBinding" bindingConfiguration="SAPBinding" contract="Rfc"
name="SAPBinding_Rfc" />
</client>
</system.serviceModel>
</configuration>
And for the web client app:
<system.serviceModel>
<behaviors />
<extensions />
<bindings>
<basicHttpBinding>
<binding name="RfcEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
<message clientCredentialType="UserName" algorithmSuite="Default"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://servername/SAP_Service_HTTP/Rfc.svc"
binding="basicHttpBinding" bindingConfiguration="RfcEndpoint"
contract="ServiceReference1.Rfc" name="RfcEndpoint" >
</endpoint>>
</client>
</system.serviceModel>
This uses transport security (SSL) to protect the headers, but it works without also.
Note the final twist to the exercise. The HTTP header values needed to be Base64 encoded! I've no idea why, but it must be that the SAP adapter is expecting them to be.
Doug
Found an even easier solution!
WCF is happy to send the Username / Password credentials in the message body providing the message is protected. Don't know why I didn't think of it before, but setting security like this:
<security mode="TransportWithMessageCredential">
means that Rohit's suggestion now works.
So, the service web.config expects the credentials in the message and uses transport security:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<services>
<service behaviorConfiguration="customServiceBehavior" name="RfcClient">
<endpoint address="" behaviorConfiguration="customEndpointBehavior"
binding="basicHttpBinding" bindingConfiguration="RfcClientBindingConfig"
name="RfcEndpoint" contract="Rfc" />
<endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<endpointBehaviors>
<behavior name="customEndpointBehavior">
<endpointBehavior adapterSecurityBridgeType="ClientCredentialUsernamePassword" />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="customServiceBehavior">
<serviceMetadata httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials type="Microsoft.ServiceModel.Channels.AdapterServiceCredentials, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<serviceCertificate findValue="servername" storeLocation="LocalMachine"
storeName="My" x509FindType="FindBySubjectName" />
</serviceCredentials>
<serviceAuthorization serviceAuthorizationManagerType="Microsoft.ServiceModel.Channels.AdapterServiceAuthorizationManager, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
<authorizationPolicies>
<add policyType="Microsoft.ServiceModel.Channels.AdapterAuthorizationPolicy, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</authorizationPolicies>
</serviceAuthorization>
</behavior>
</serviceBehaviors>
</behaviors>
<extensions>
<behaviorExtensions>
<add name="endpointBehavior" type="Microsoft.ServiceModel.Channels.AdapterEndpointBehavior, Microsoft.ServiceModel.Channels, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</behaviorExtensions>
</extensions>
<bindings>
<basicHttpBinding>
<binding name="RfcClientBindingConfig">
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" />
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
<sapBinding>
<binding name="SAPBinding" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" enableBizTalkCompatibilityMode="false"
receiveIdocFormat="Typed" enableSafeTyping="false" generateFlatFileCompatibleIdocSchema="true"
maxConnectionsPerSystem="50" enableConnectionPooling="true"
idleConnectionTimeout="00:15:00" flatFileSegmentIndicator="SegmentDefinition"
enablePerformanceCounters="false" autoConfirmSentIdocs="false"
acceptCredentialsInUri="false" padReceivedIdocWithSpaces="false"
sncLibrary="" sncPartnerName="" rfcAllowStartProgram="">
<dataTypesBehavior datsMinToDateTime="NULL" datsMaxToDateTime="NULL"
invalidDatsToDateTime="ERROR" emptyDatsToDateTime="0001-01-01T00:00:00"
emptyTimsToDateTime="0001-01-01T00:00:00" dateTimeMaxToDats="99991231"
dateTimeMinToDats="00010101" timsMaxToDateTime="NULL" invalidTimsToDateTime="ERROR"
dateTimeMaxToTims="235959" dateTimeMinToTims="000000" invalidNumcToInt="0"
emptyNumcToInt="0" dateTimeNullToDats="SKIP" dateTimeNullToTims="SKIP" />
</binding>
</sapBinding>
</bindings>
<client>
<endpoint address="sap://CLIENT=300;LANG=EN;#a/XXXXXX/00?RfcSdkTrace=False&AbapDebug=False"
binding="sapBinding" bindingConfiguration="SAPBinding" contract="Rfc"
name="SAPBinding_Rfc" />
</client>
</system.serviceModel>
</configuration>
And the client web.config is similarly configured:
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="RfcEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
<message clientCredentialType="UserName" algorithmSuite="Default"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://servername/SAP_Service_BASIC/Rfc.svc" binding="basicHttpBinding" bindingConfiguration="RfcEndpoint" contract="ServiceReference1.Rfc" name="RfcEndpoint"/>
</client>
</system.serviceModel>
Then the client code is simply:
string cust = "12345";
string po = "12345";
string salesOrg = "1234";
string transGroup = "0";
BAPIORDERS[] orders = new BAPIORDERS[0];
BAPIRETURN ret = new BAPIRETURN();
using (RfcClient client = new RfcClient("RfcEndpoint"))
{
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
try
{
ret = client.BAPI_SALESORDER_GETLIST(cust, null, null, null, po, null, salesOrg, transGroup, ref orders);
GridView1.DataSource = orders;
GridView1.DataBind();
Label1.Text = DateTime.Now.ToString();
client.Close();
}
catch (CommunicationException ex)
{
StringBuilder sb = new StringBuilder();
sb.Append(ex.Message);
if (ex.InnerException != null) sb.Append("~" + ex.InnerException.Message);
Label1.Text = sb.ToString();
client.Abort();
}
catch (TimeoutException ex)
{
Label1.Text = ex.Message;
client.Abort();
}
catch (Exception ex)
{
Label1.Text = ex.Message;
client.Abort();
throw;
}
}
Perfect!
Doesn't adding the following to the client app work for you?
RfcClient client = new RfcClient();
client.ClientCredentials.UserName.UserName = "myusername";
client.ClientCredentials.UserName.Password = "mypassword";