I've got a strange problem: I have a wcf service which needs to be hosted by tcp protocol on a standalone console application.
I have created a new console app project and added a lib reference for my wcf project. The problem occurs when I want to configure and broadcast the host.
var Uri = new Uri("net.tcp://myaddress:4322/MyService");
MyServiceHost = new ServiceHost(typeof(ImyService), Uri);
var binding = new NetTcpBinding();
MyServiceHost.AddServiceEndpoint(typeof(IImageExchangeService), binding, "");
var mexBinding = MetadataExchangeBindings.CreateMexTcpBinding();
MyServiceHost.AddServiceEndpoint(typeof(IMetadataExchange),mexBinding, "");
ServiceMetadataBehavior serviceBehavior = new ServiceMetadataBehavior();
serviceBehavior.HttpGetEnabled = true;
serviceBehavior.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
MyServiceHost.Description.Behaviors.Add(serviceBehavior);
When it comes to open the host, I get this message:
The contract name 'IMetadataExchange' could
not be found in the list of contracts implemented by the service WcfService.Ser
vices.MyService. Add a ServiceMetadataBehavior to the configuration
file or to the ServiceHost directly to enable support for this contract.
Now that I have better time to check this.
First you need to get the service behavior from the host or create a new one and add it if not found.
Setting the HttpGetEnabled requires you to also ad a metadata uri to the baseaddresses.
Also it does not seem to matter if the metadata endpoint is named "mex", as long as all endpoints added have unique names. So you can't use empty string for both endpoints.
var Uri = new Uri("net.tcp://myaddress:4322/MyService");
var MetadataUri = new Uri("http://myaddress:8000/MyService");
var MyServiceHost = new ServiceHost(typeof(MyService), Uri, MetadataUri);
var serviceBehavior = MyServiceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (serviceBehavior == null)
{
serviceBehavior = new ServiceMetadataBehavior();
MyServiceHost.Description.Behaviors.Add(serviceBehavior);
}
serviceBehavior.HttpGetEnabled = true;
serviceBehavior.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
MyServiceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), "mex");
var binding = new NetTcpBinding();
MyServiceHost.AddServiceEndpoint(typeof(IImageExchangeService), binding, "image");
Related
I'm trying to get my generated WCF ServiceClient to send its requests to the WCF service through a HTTP (not HTTPS) proxy with username/password authentication, however I just can't get it work. My WCF service uses basicHttpBinding so I tried to configure my ServiceClient instance like so:
var svc = new ServiceClient();
var b = svc.Endpoint.Binding as BasicHttpBinding;
b.ProxyAddress = new Uri(proxyAddress);
b.UseDefaultWebProxy = false;
b.Security.Mode = BasicHttpSecurityMode.Transport;
b.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
b.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
svc.ClientCredentials.UserName.UserName = proxyUsername;
svc.ClientCredentials.UserName.Password = proxyPassword;
This however results in a System.ArgumentException saying:
The provided URI scheme http is invalid. expected https
When I set the b.Security.Mode to BasicHttpSecurityMode.None though, it seems the HTTP proxy settings are ignored by WCF altogether!
The second solution I tried was to set the DefaultWebProxy property of WebRequest and set the UseDefaultWebProxy property to true such as so:
var webProxy = new WebProxy(proxyAddress, true);
webProxy.Credentials = new NetworkCredential(proxyUsername, proxyPassword);
WebRequest.DefaultWebProxy = webProxy;
var svc = new ServiceClient();
var b = svc.Endpoint.Binding as BasicHttpBinding;
b.UseDefaultWebProxy = true;
However this also doesn't work and ServiceClient doesn't send it's requests through the HTTP proxy.
I'm out of ideas here so please let me know what I am doing wrong, thank you!
Set the security mode to BasicHttpSecurityMode.TransportCredentialOnly. This allows for passing plain-text authentication details over HTTP.
In the code below I get an error. I guess it is something simple or impossible to do, because I wasn't able to find anything with Google. Anyway the error I am getting is:
The contract name 'SchippersStop.Wcf.Balie.Interfaces.IBalie' could not be found in the list of contracts implemented by the service 'System.ServiceModel.Routing.RoutingService'.
The error occurs when I try to add a ServiceEndpoint to the host.
ServiceHost host = new ServiceHost(typeof(RoutingService));
string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
foreach (string dll in Directory.GetFiles(path, "*.dll"))
{
Assembly assembly = Assembly.LoadFile(dll);
foreach (Type type in assembly.GetTypes())
{
if (type.GetInterfaces().Contains(typeof(ISchippersStopSvc)))
{
DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
FindCriteria findCriteria = new FindCriteria(type);
findCriteria.Duration = TimeSpan.FromSeconds(10);
FindResponse findResponse = discoveryClient.Find(findCriteria);
EndpointDiscoveryMetadata discoveryMetadata = findResponse.Endpoints.First();
Uri uri = discoveryMetadata.Address.Uri;
string routerAddress = uri.AbsoluteUri.Replace(uri.Host, "0.0.0.0");
DiscoveryClientBindingElement discoveryBindingElement = new DiscoveryClientBindingElement();
discoveryBindingElement.FindCriteria = findCriteria;
discoveryBindingElement.DiscoveryEndpointProvider = new UdpDiscoveryEndpointProvider();
CustomBinding binding = new CustomBinding();
binding.Elements.Insert(0, discoveryBindingElement);
ContractDescription contract = ContractDescription.GetContract(type);
RoutingConfiguration rc = new RoutingConfiguration();
rc.RouteOnHeadersOnly = true;
string groupName = Guid.NewGuid().ToString();
foreach (EndpointDiscoveryMetadata endpoint in findResponse.Endpoints)
{
List<ServiceEndpoint> endpointList = new List<ServiceEndpoint>();
ServiceEndpoint client = new ServiceEndpoint(contract, binding, new EndpointAddress(endpoint.Address.Uri.AbsoluteUri));
endpointList.Add(client);
endpointList.AddRange(findResponse.Endpoints.Except(new List<EndpointDiscoveryMetadata>() { endpoint }).Select(s => new ServiceEndpoint(contract, binding, new EndpointAddress(s.Address.Uri.AbsoluteUri))).ToList());
endpointList.ForEach(f => f.EndpointBehaviors.Add(new ClientTrackerEndpointBehavior() { }));
rc.FilterTable.Add(new CustomMessageFilter(groupName), endpointList);
}
RoutingBehavior routing = new RoutingBehavior(rc);
host.Description.Behaviors.Add(routing);
// Here the error occurs
host.AddServiceEndpoint(type, binding, routerAddress);
}
}
}
The goal is to add WCF services that are discovered on the network to the router. However I would like to set it up in such a way that I don't need to know anything about the services and the router can detect all the necessary configurations after which it can dynamically load them. The only thing I want to provide to the router is the ServiceContract. The router can detect the assemblies with a ServiceContract based on the common interface they inherit from.
Can someone help me to solve this error?
I want to create a class that implementing 2 service contracts, and exposing 2 end-points on different ports (I get the ports as input to the program).
My question is very similar to this question, but I want two end-points each one on different port.
EDIT:
Using this code:
var aConnectionString = "http://localhost:8200/A";
var bConnectionString = "http://localhost:8201/B";
ServiceHost host= new ServiceHost(typeof(abImp));
var binding = new BasicHttpBinding();
host.AddServiceEndpoint(typeof(A), binding, aConnectionString);
host.AddServiceEndpoint(typeof(B), binding, bConnectionString);
{
// Check to see if the service host already has a ServiceMetadataBehavior
ServiceMetadataBehavior smb = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
// If not, add one
if (smb == null)
smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
host.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(),
aConnectionString
);
}
host.Open();
I'm getting this error message on the host.Open() operation:
An unhandled exception of type 'System.InvalidOperationException' occurred in System.ServiceModel.dll
Additional information: A binding instance has already been associated to listen URI 'http://localhost:8200/A'. If two endpoints want to share the same ListenUri, they must also share the same binding object instance. The two conflicting endpoints were either specified in AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint() and config.
SOLUTION
var aConnectionString = "http://localhost:" + portA+ "/A";
var bConnectionString = "http://localhost:" + portB+ "/B";
ServiceHost aHost = new ServiceHost(instance, new Uri(aConnectionString));
ServiceHost bHost = new ServiceHost(instance, new Uri(bConnectionString));
aHost.Description.Behaviors.Find<ServiceBehaviorAttribute>().InstanceContextMode = InstanceContextMode.Single;
bHost.Description.Behaviors.Find<ServiceBehaviorAttribute>().InstanceContextMode = InstanceContextMode.Single;
aHost.AddServiceEndpoint(typeof(A), new BasicHttpBinding(), aConnectionString);
bHost.AddServiceEndpoint(typeof(B), new BasicHttpBinding(), bConnectionString);
{
// Check to see if the service host already has a ServiceMetadataBehavior
var smb = aHost.Description.Behaviors.Find<ServiceMetadataBehavior>() ?? new ServiceMetadataBehavior();
aHost.Description.Behaviors.Add(smb);
aHost.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(),
aConnectionString + "/Mex"
);
}
{
// Check to see if the service host already has a ServiceMetadataBehavior
var smb = bHost.Description.Behaviors.Find<ServiceMetadataBehavior>() ?? new ServiceMetadataBehavior();
bHost.Description.Behaviors.Add(smb);
bHost.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(),
bConnectionString + "/Mex"
);
}
aHost.Open();
bHost.Open();
Given that you want two completely different addresses, only same implementation class, I'd recommend use of two different ServiceHost instances.
I would like to setup a module which will communicate with other modules remotely acting both as a service and as a client. The communcation should go in SOAP 1.2 and it should use OASIS WSS 1.1, and X.509 certificate token profile.
OASIS WSS 1.1 X.509 specs
I already made a development certificate using makecert and it is trusted already.
Since the module is essentially C# based, all settings are given in code. So far I got the following service code:
The code for the binding:
System.ServiceModel.Channels.AsymmetricSecurityBindingElement asbe = new AsymmetricSecurityBindingElement();
asbe.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12;
asbe.InitiatorTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient };
asbe.RecipientTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient };
asbe.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
asbe.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
asbe.EnableUnsecuredResponse = true;
asbe.IncludeTimestamp = false;
asbe.SetKeyDerivation(false);
asbe.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128Rsa15;
asbe.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters());
asbe.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters());
CustomBinding myBinding = new CustomBinding();
myBinding.Elements.Add(asbe);
myBinding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap12, Encoding.UTF8));
HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
httpsBindingElement.RequireClientCertificate = true;
myBinding.Elements.Add(httpsBindingElement);
The code for the behaviour:
//Then initiate the service host
_Host = new ServiceHost(typeof(TClass), baseAddress);
//Add the service endpoint we defined
_Host.AddServiceEndpoint(typeof(TInterface), _Binding, typeof(TInterface).ToString());//BindingHelper.GetUserNameBinding(), "");
//Set searching the certificate
_Host.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "MyServerCert");
_Host.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
_Host.Credentials.ClientCertificate.Authentication.RevocationMode = X509RevocationMode.NoCheck;
//Allow the metadata spreading
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpsGetEnabled = true;
smb.HttpGetEnabled = true;
_Host.Description.Behaviors.Add(smb);
ServiceDebugBehavior sdb = new ServiceDebugBehavior();
sdb.IncludeExceptionDetailInFaults = false; //Should only provide the endpoint property (GP WS-Message profile specs)
//Add the appropriate endpoint
if (baseAddress.AbsoluteUri.Contains("https"))
_Host.AddServiceEndpoint(
typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexHttpsBinding(),
"mex");
else
_Host.AddServiceEndpoint(
typeof(IMetadataExchange),
MetadataExchangeBindings.CreateMexHttpBinding(),
"mex");
On the client side I use the same code to create the binding, plus I use the following behavior:
channelFactory = new ChannelFactory<T>(bindIn, serviceAddress);
if (wsFeature != null)
{
channelFactory.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
channelFactory.Endpoint.Behaviors.Add(wsFeature);
channelFactory.Credentials.ClientCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "MyServerCert");
channelFactory.Credentials.ServiceCertificate.SetScopedCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindBySubjectName, "MyServerCert",serviceAddress.Uri);
}
_ProxiObject = channelFactory.CreateChannel();
Here basically the behavior wsFeature is a simple class doing practically nothing (just implements blank functions for IEndpointBehavior).
I have both service and client on the same machine at https://localhost.:8084/testhosting4, and even though both service and client are successfully created I only got the famous "An error occurred while making the HTTP request to https://localhost.:8084/testhosting4. etc." error.
I already managed to connect via an unsecure channel with the module (BasicHttpBinding - no security) and exchange messages, so I am sure that I make a mistake in defining the binding or assigning credentials. Obviously, I browsed a lot here already, but couldn't come up with a working solution.
This is the first time I meet WCF and X509 and I'm not in secure communications at all either. So plenty of occasions to make a mistake. Please point out those which I've made.
Thank you!
I got a service which offers a named pipe as connection point/endpoint address for 3rd party applications.
what i did so far is:
Uri[] baseAddress = new Uri[]{
new Uri("net.pipe://localhost")};
string PipeName = "Calculator";
serviceHost = new ServiceHost(typeof(CalculatorImplementation), baseAddress);
// Add a mex endpoint
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.HttpGetUrl = new Uri("http://localhost:8001/CalculatorServer");
serviceHost.Description.Behaviors.Add(smb);
serviceHost.AddServiceEndpoint(typeof(ICalculator), new NetNamedPipeBinding(), PipeName);
serviceHost.Open();
I'm connecting in my client using a ChannelFactory like this:
ChannelFactory<ICalculator> pipeFactory = new ChannelFactory<ICalculator>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/Calculator"));
ICalculator pipeProxy = pipeFactory.CreateChannel();
But acutally I wonder if there is a more "usual" way to connect to the service.
Does I really need to tell every 3rd party app the "connection string"/Endpoint Address of my named pipe ("net.pipe://localhost/Calculator") ?
I thought that there might be a better way to accomplish this, but I did not found a way to do this so far.
I created already that "mex endpoint" but I'm not sure how to use this endpoint to populate the named pipe.
My questions are:
I'm populating the service interface the right way?
and
How do I use the Metadata Endpoint in a client application right?