WCF Client Side Configuration for different Endpoint URI - c#

I am in a situation where I need to develop a WCF Client which will have different EndPoint URI but other settings would remain same. I would get the EndPoint URI from the user.
So I wanted to know if I consume the WCF service using ChannelFactory, then do I need to have app.config file which would contain the WCF Client side configuration with only one endpoint and the address attribute would be blank (which I would get as input from the user) Or do I need to go for programmatically consuming the service.

Leave the endpoint blank in config file. In your code add a method like the one below that takes endpointAddress as a parameter which can come from the user. Use this method to create the channelfactory that you will eventually use to create proxy
private ChannelFactory<IService1> GetChannelFactory(string endpointAddress)
{
// create a binding that will be common
BasicHttpBinding myBinding = new BasicHttpBinding();
//get your uri from the user
EndpointAddress myEndpoint = new EndpointAddress(endpointAddress);
ChannelFactory<IService1> myChannelFactory = new ChannelFactory<IService1>(myBinding, myEndpoint);
return myChannelFactory;
}

Related

Converting a WCF service to console application

I have a WCF service. Now I would like to convert it to console application. Basically, it is a simple service. It only does 2 jobs depending on client request by PowerShell request.
Invoke-WebRequest "http://localhost:10000/backup/DoBackup?id=BackupClient1"
or
Invoke-WebRequest "http://localhost:10000/backup/DoBackup?id=BackupClient2"
My service listens on request for this 2 requests.
I have this WCF code:
myService service = new myService(_settings);
backupServiceHost = new WebServiceHost(service, _backupAddress);
WebHttpBinding binding = new WebHttpBinding();
string address = "";
ServiceEndpoint endpoint = backupServiceHost .AddServiceEndpoint(typeof(ISnapshotServiceContract), binding, address);
backupServiceHost .Open();
I can't use WebServiceHost and WebHttpBinding in my console app. What is the best replacement for this? I'm thinking to use simple HttpClient. But I'm not sure is it a correct selection?
why don't use command line parameters and avoid WebServiceHost altogether? You can then just compare your args[] from Main function (see e.g. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/main-and-command-args/) to determine if it is BackupClient1 command or BackupClient2 command.
If you want to take this concept further you can extract functionality of BackupClient1 command and BackupClient2 command into separate library and have frontends (WCF and command line) using same backend.
If you really need WCF service (as stated by comments below) please refer to this MSDN article; you will have to provide your configuration and figure out how to indicate that you wish to end the process in nice way but it should boil down to something like this:
String baseAddress = "http://localhost:10000/backup";
BasicHttpBinding binding = new BasicHttpBinding();
using (ServiceHost host = new ServiceHost(typeof(myService)))
{
host.AddServiceEndpoint(typeof(IMyService), binding, baseAddress);
host.Open();
while(true) {
// Figure out how to exit this loop - e.g. listen to some command from service
}
}

Onvif SOAP request with SOAP level authentication and HTTP authentication

This question has been discussed in several topics here but I could not find the answer for me.
What I'm trying to do is use an IP camera through the Onvif interface. I've generated the web services from the WSDL files available in the Onvif homepage, and added the custom SOAP authentication code as suggested here, and I am able to retrieve the device capabilities etc. etc.
But for some services, e.g, PTZ control, also HTTP authentication is needed. My code removes the ClientCredentials behaivor (so yeah, I guess setting them does not make any sense, but I still left those lines in hope that maybe the HTTP transport would try to use them):
HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();
httpBindingElement.AuthenticationScheme = AuthenticationSchemes.Basic;
...
PTZClient ptzClient = new PTZClient(customBinding, endPointAddress);
ptzClient.Endpoint.Behaviors.Remove(typeof(System.ServiceModel.Description.ClientCredentials));
UsernameClientCredentials onvifCredentials = new UsernameClientCredentials(new UsernameInfo(_username, _password));
ptzClient.Endpoint.Behaviors.Add(onvifCredentials);
ptzClient.ClientCredentials.UserName.UserName = _username;
ptzClient.ClientCredentials.UserName.Password = _password;
Still when I look at wireshark, i see that the SOAP authentication is generated but no HTTP authentication header is set (well, I already expected that since i have a custom behaivor here). So the question is, if I am creating the binding this way, what are my best options to add HTTP authentication headers? Can I just add a message inspector, and if so, any examples? Must I create a different transport binding? I've seen people advising others to use BasicHttpBinding and then setting the Security property on that, but where do the credentials go in that case and how do I apply the BasicHttpBinding instance to my binding? Are there any callbacks in the WCF that get triggered by the HTTP 401 code that i can hook up to and then provide the header? This is actually my first experience with WCF and so far I've done everything from examples found in the internet, but as for this particular issue I haven't been able to find anything.
If anyone is interested this is how I got it working. I combined the BasicHttpBinding with the client credentials in a following way:
TransportSecurityBindingElement transportSecurity = new TransportSecurityBindingElement();
// UsernameCredentials is a class implementing WS-UsernameToken authentication
transportSecurity.EndpointSupportingTokenParameters.SignedEncrypted.Add(new UsernameTokenParameters());
transportSecurity.AllowInsecureTransport = true;
transportSecurity.IncludeTimestamp = false;
TextMessageEncodingBindingElement messageEncoding = new TextMessageEncodingBindingElement(MessageVersion.Soap12, Encoding.UTF8);
HttpClientCredentialType[] credentialTypes = new HttpClientCredentialType[3] { HttpClientCredentialType.None, HttpClientCredentialType.Basic, HttpClientCredentialType.Digest };
...
foreach (HttpClientCredentialType credentialType in credentialTypes)
{
BasicHttpBinding httpBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
httpBinding.Security.Transport.ClientCredentialType = credentialType;
BindingElementCollection elements = new BindingElementCollection(new BindingElement[1]{messageEncoding});
foreach(BindingElement element in httpBinding.CreateBindingElements())
{
if (element is TextMessageEncodingBindingElement)
continue;
elements.Add(element);
}
CustomBinding customBinding = new CustomBinding(elements);
DeviceClient deviceClient = new DeviceClient(customBinding, endPointAddress);
if (credentialType == HttpClientCredentialType.Basic)
{
// Set all credentials, not sure from which one WCF actually takes the value
deviceClient.ClientCredentials.UserName.UserName = pair[0];
deviceClient.ClientCredentials.UserName.Password = pair[1];
}
else if (credentialType == HttpClientCredentialType.Digest)
{
deviceClient.ClientCredentials.HttpDigest.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Delegation;
deviceClient.ClientCredentials.HttpDigest.ClientCredential.UserName = pair[0];
deviceClient.ClientCredentials.HttpDigest.ClientCredential.Password = pair[1];
}
}
This works efficiently with a device for which we do not know the authentication mode and works on both (HTTP/SOAP) authentication level.
I detailed how HTTP digest works in another answer.
Remember that only functions of class PRE_AUTH, according to §5.12.1 of the Core spec, require authentication.
You should invoke a function of any class but PRE_AUTH without any form authentication. If you get a HTTP 401 then you have to use HTTP digset, otherwise you'll have to got with WS-UsernameToken.
You can't directly use HTTP digest because you'll need at least the device to send you the challange for HTTP digest.

Difference in authentication of WCF Channel Factory vs Service Reference

I am consuming a 3rd party https web service in a WCF service using the 2 ways described below.
Using Service Reference
ServiceClient client=new ServiceClient();
client.ClientCredentials.UserName.UserName ="xxx";
client.ClientCredentials.UserName.Password = "pwd";
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
ServiceResponse response=client.GetData();
2.Using channel factory
ChannelFactory<IService> client = new ChannelFactory<IService>(binding, address);
var proxy = client.CreateChannel();
client.Credentials.UserName.UserName ="xxx";
client.Credentials.UserName.Password ="pwd";
ServiceResponse response=client.GetData();
I am able to pass the security credentials using the first approach and i am able to get the respone back from the 3rd party web service.But I am unable to get the response when i use the second approach. I can see that the username,password are added in the security header of the outoing SOAP message with the first approach but not with the second approach.I would be glad if some one can throw some suggestion here about the channel factory approach.
The issue is when you're assigning the credentials - in your current code, you're creating the proxy after you create the factory, and then you assign the credentials to the factory. But that has no effect on the created channel:
ChannelFactory<IService> client = new ChannelFactory<IService>(binding, address);
var proxy = client.CreateChannel();
client.Credentials.UserName.UserName ="xxx";
client.Credentials.UserName.Password ="pwd";
var proxy is a an implementation of IChannel - setting the credentials on the factory (client) has no effect on the already created channels - just the ones created later.
Try setting the credentials and then calling CreateChannel():
ChannelFactory<IService> client = new ChannelFactory<IService>(binding, address);
client.Credentials.UserName.UserName ="xxx";
client.Credentials.UserName.Password ="pwd";
var proxy = client.CreateChannel();
ServiceResponse response=client.GetData();

Dynamically creating endpoints for Magento in C#

I need to dynamically set the endpoint for my Magento implementation using C# but can't override C#'s default check of the endpoint path and credentials in the web.config.
Does anyone know how to do this?
My service currently looks like this:
using (Mage_Api_Model_Server_V2_HandlerPortTypeClient proxy = new Mage_Api_Model_Server_V2_HandlerPortTypeClient("NameOfEndpoint", ConnectionCurrent.WsdlPath))
{
string sessionKey = proxy.startSession();
string loginSession = proxy.login(ConnectionCurrent.UserName, ConnectionCurrent.Password);
...
At Login, it then says that I have two endpoints configured.
I've looked everywhere but can't find a solution.
Thanks!!
This is using WCF but it is similarly done with the older web services implementation:
EndpointAddress endPoint = new EndpointAddress("http://some.endpoint.addr");
Binding binding = new WSHttpBinding(SecurityMode.None);
var service = new Mage_Api_Model_Server_V2_HandlerPortTypeClient(binding, endpoint);

Dynamically switch WCF Web Service Reference URL path through config file

How do you dynamically switch WCF Web Service Reference URL path through config file ?
Are you just wanting to override the URL that is in the config to a different url. Say you have a test service and a live service. You can just do this.
client.Endpoint.Address = new EndpointAddress(Server.IsLiveServer() ?
#"LiveUrl" : #"TestURl");
Where those url come from wherever you want
Just to expand on the answer from Erin: -
MyClient client = new MyService.MyClient();
client.Endpoint.Address = new EndpointAddress(new Uri("insert new url here"),
client.Endpoint.Address.Identity, client.Endpoint.Address.Headers);
client.Open();
HTH!
There is no dynamic switching. Each time you want to use another URL you must create new instance of service proxy (client) and pass EndpointAddress or enpoint configuration name to the constructor.
I have been trying to do the same thing but most of the accepted answers in various posts just change the address. Currently under .net 4.7, simply changing the address itself does not work. If you have two different servers and wanting it to switch from one to the other, you have to do this:
var client = new MyService.Service1Client();
var newAdrEndpoint = new EndpointAddress(new Uri("second server address"));
client = new MyService.Service1Client(client.Endpoint.Binding, newAdrEndpoint);
Essentially you need to create a new service using the same binding from the first server and passing in the new address. This is the simplest method I have found.
sure you can do this, have a look here: How to config clients for a wcf service?
it is absolutely normal to point to localhost in development and to change the address (url) in production in the web.config
you can´t chance endpoint url after any calling.
E.G.
in that case, you will get answer from NEWURL:
MyClient client = new MyService.MyClient();
client.Endpoint.Address = new EndpointAddress("NEWURL");
client.Hello(); //return is hello response from NEWURL
but if you will call any method before changing url, the url will be used from app.config, like next example:
MyClient client = new MyService.MyClient();
client.Endpoint.Address = new EndpointAddress("NEWURL");
client.Hello(); //return is hello response from BASEURL

Categories