Consuming Web Services - Confusion on Setup - c#

I recently added a remote WSDL file for a web service to my app_webreferences folder.
I can see all the objects in that WSDL file - however, the problem is that the company that i'm integrating with sent me two urls.
a wsdl: https://website.com/SOAP/Queue?wsdl
an endpoint: https://website.com/SOAP/Queue?api_key=xxxxxx
So - when I added the app_webreference I used the first url - it worked, added it to project.
I then proceeded to setup my bindings as such:
<wsHttpBinding>
<binding name="QueueWebServiceBinding">
<security mode="TransportWithMessageCredential">
<message clientCredentialType="UserName" />
</security>
</binding>
</wsHttpBinding>
My client endpoint looks like this:
<endpoint address="https://website.com/SOAP/Queue?wsdl"
binding="wsHttpBinding" bindingConfiguration="QueueWebServiceBinding"
contract="MyWebService" name="QueueWebServicePort" />
</client>
The thing is, I cannot for the life of me get this to work. I'm not sure where I should use the endpoint url (#2) in any of this. It seems that when I switch it out in the endpoint address (#2) above I receive a response of:
...<env:Fault..... does not contain operation meta data for ... </env:Fault>...
I'm really confused on how to set this all up.
Another thing to note, they do require authentication... so my actual code looks something like this...
MyWebServiceClient client = new MyWebServiceClient();
client.ClientCredentials.UserName.UserName = "username";
client.ClientCredentials.UserName.Password = "password";
var t = client.doSomething();
I've been googling/searching stackoverflow for the past few hours but have had little success figuring out the right way to set this all up.
Appreciate any help.

The first endpoint is for the WSDL. The second endpoint is for the actual service itself. So you need to change your endpoint in your config file as follows:
<client>
<endpoint address="https://website.com/SOAP/Queue?api_key=xxxxxx"
binding="wsHttpBinding"
bindingConfiguration="QueueWebServiceBinding"
contract="MyWebService" name="QueueWebServicePort" />
</client>
Since you've already tried switching out the endpoint, try using the second endpoint to add the service reference and see if that works (if you haven't already).
If neither of those work, then I'd suggest talking to the vendor's support staff to see if they can help, since they should know everything you need to do to access the service.

Related

Using Both HTTP and HTTPS in same WCF Binding - Changing <security mode="Transport"> to <security mode="TransportCredentialOnly"> In Code

I have an autogenerated binding from a WSDL file in a Visual Studio project that looks like this:
<binding name="XServiceSoap"
maxBufferPoolSize="16777216"
maxBufferSize="16777216"
maxReceivedMessageSize="16777216">
<security mode="TransportCredentialOnly">
<transport clientCredentialType="Ntlm"
proxyCredentialType="None"/>
</security>
</binding>
and the client looks like this:
<endpoint address="http://uri/XService.asmx"
binding="basicHttpBinding"
bindingConfiguration="XServiceSoap"
contract="XService.XServiceSoap"
name="XServiceSoap"/>
In the code, I'm easily able to change the endpoint address (and credentials) using
using WPFApp.XService;
var xServiceSoapClient = new XServiceSoapClient("XServiceSoap");
xServiceSoapClient.Endpoint.Address = new EndpointAddress(NEW_ENDPOINT);
xServiceSoapClient
.ClientCredentials.Windows.ClientCredential.UserName = NEW_USERNAME;
xServiceSoapClient
.ClientCredentials.Windows.ClientCredential.Password = NEW_PASSWORD;
But if I try using a non-http endpoint, I got thrown an exception The provided URI scheme 'https' is invalid; expected 'http'.
This error makes sense to me (ex. from here), but I want the same binding to support both HTTP and HTTPS when I change the endpoint (test vs prod server). How do I do this in code with the built in soap client? I don't see an option in the xServiceSoapClient to change the security mode, ex. I don't see a XServiceSoapclient.Security.Mode option where it can be changed.
I can confirm when I change the app.config manually from TransportCredentialOnly to Transport, https connections work without throwing an exception.
Thank you!
The constructor for the WCF client has an overload that takes two parameters, binding and endpoint
you can use this overload to pass your desired security mode:
WSHttpBinding binding = new WSHttpBinding();
binding.Security.Mode = SecurityMode.Transport;
var xServiceSoapClient = new xServiceSoapClient(binding, "XServiceSoap");
This is the general idea, using the designated object rather then the service client object that is not meant for the above kind of manipulations.

BasicHttpBinding from app.config inaccessible in code

I have several web services that I am connecting to from a Visual Studio C# project using service references. Two of the service references were created and work without a problem, and one of them took quite a lot of effort to get imported, and now seems to not be working.
I believe the problem lies in the app.config file since it is getting a "Could not find endpoint element" error when I try to create the client.
Here is the app.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="RateQuoteSoap">
<security mode="Transport" />
</binding>
<binding name="RateQuoteSoap1" />
<binding name="QuoteSoap" />
<binding name="WebservicePrimusSoapBinding" />
</basicHttpBinding>
<customBinding>
<binding name="QuoteSoap12">
<textMessageEncoding messageVersion="Soap12" />
<httpTransport />
</binding>
</customBinding>
</bindings>
<client>
<endpoint
address="https://webservices.rrts.com/rating/ratequote.asmx"
binding="basicHttpBinding"
bindingConfiguration="RateQuoteSoap"
contract="RoadRunnerService.RateQuoteSoap"
name="RateQuoteSoap" />
<endpoint
address="http://services.echo.com/Quote.asmx"
binding="basicHttpBinding"
bindingConfiguration="QuoteSoap"
contract="EchoService.QuoteSoap"
name="QuoteSoap" />
<endpoint
address="http://services.echo.com/Quote.asmx"
binding="customBinding"
bindingConfiguration="QuoteSoap12"
contract="EchoService.QuoteSoap"
name="QuoteSoap12" />
<endpoint
address="http://api.shipprimus.com/"
binding="basicHttpBinding"
bindingConfiguration="WebservicePrimusSoapBinding"
contract="PrimusService.WebservicePrimusServicePort"
name="WebservicePrimusServicePort" />
</client>
</system.serviceModel>
</configuration>
The PrimusService is the one that is not working correctly, and the full error when I try to initialize the client like WebservicePrimusServicePortClient serviceClient = new WebservicePrimusServicePortClient("WebservicePrimusServicePort"); is
System.InvalidOperationException: Could not find endpoint element with name 'WebservicePrimusServicePort' and contract 'PrimusService.WebservicePrimusServicePort'in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this name could be found in the client element.
I have also tried to simply initialize a BasicHttpBinding object using the binding name and the endpoint name with no luck (short variable names for readability)
BasicHttpBinding a = new BasicHttpBinding("QuoteSoap"); // Works fine
BasicHttpBinding b = new BasicHttpBinding("WebservicePrimusSoapBinding"); // Fails
BasicHttpBinding c = new BasicHttpBinding("WebservicePrimusServicePort"); // Fails
It throws no error for binding a, but binding b and c fail with the error:
System.Collections.Generic.KeyNotFoundException: No elements matching the key 'WebservicePrimusSoapBinding' were found in the configuration element collection.
While not a direct solution, I ended up just taking the information from the app.config and creating my own BasicHttpBinding and EndpointAddress objects in code. This is more of a workaround than a fix for the problem, and I still don't know why I couldn't access the information in the app.config directly.
I followed the solution in this answer about how to consume a service without using the app.config file.
I created my BasicHttpBinding like
BasicHttpBinding binding = new BasicHttpBinding();
binding.Name = "PrimusServiceBinding"; // Completely Unnecessary
and my endpoint like
EndpointAddress endpoint = new EndpointAddress("http://api.shipprimus.com/");
and could connect to the service and retrieve information without a problem, even providing as little information as I did (basically just the address).

Issue with service reference

I created console application to consume a web service. When I invoke that web service I am getting below exception
The envelope version of the incoming message (Soap11 (http://schemas.xmlsoap.org/soap/envelope/)) does not match that of the encoder (Soap12 (http://www.w3.org/2003/05/soap-envelope)). Make sure the binding is configured with the same version as the expected messages.
This is my configuration. Can you please help me to fix this?
<wsHttpBinding>
<binding name="SecurityDemo">
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
<endpoint name="endpointname"
address="URL"
binding="wsHttpBinding" bindingConfiguration="SecurityDemo"
contract="contractname" />
By adopting wsHttpBinding you are basically forcing your clients to use Soap12. Change it to BasicHttpBinding and see if you get different results and keep it if you don't need the Soap12 features. Some more details can be found here
In .NET 6, I was able to do this:
// In Program.cs ...
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// ...
app.UseSoapEndpoint<IMyServiceContract>("/MyService.asmx", new SoapEncoderOptions()
{
// Use SOAP version 1.2 (aka Soap12)
MessageVersion = MessageVersion.Soap12WSAddressingAugust2004
}, caseInsensitivePath: true);

How to supply both UserName and Client Certificate in WCF client (why does this example work)?

Consider a WCF service in which the intent is to have Client Certificates required at the Transport layer (Client Certificates set to "Required" in IIS). As well, there will be username authentication at the message layer.
Now I've seen this question already:
WCF Client Certificate AND UserName Credentials forbidden
and I can somewhat understand what's going on there and realize that inherently WCF does not allow both. I went through the same steps in code as the poster in the link referenced above and found the same result...the message-level UserName credentials were being passed (in the SOAP header in my case), but the Client Cert (despite being attached when the request client is viewed in VS debug) was not actually being processed by the endpoint.
So now comes the part that has me confused. I decided to hack it somewhat. I'm wondering why this works exactly like I'm wanting...it gets past IIS Client Cert requirement, the UserName gets passed to the WCF Service and all just works. Yet WCF does not allow me to do it just using WCF config files or code (that I can find). Why?
// sets up a proxy client based on endpoint config
// basically just here to get the URL.
this.InitializeSubmitClient();
// these get used to create the HttpWebRequest
string url = this.submitClient.Endpoint.Address.ToString();
string action = "SubmitCDA";
// this deserializes an XML file which is the "shell" of SOAP document and inject username/password into SOAP Security node
XmlDocument soapEnvelopeXml = XMLHelper.CreateSoapDocument(this.txtSubmitCdaXmlFile.Text, this.txtAdAccount.Text, this.txtPassword.Text);
HttpWebRequest webRequest = XMLHelper.CreateWebRequest(url, action);
// saves the SOAP XML into the webRequest stream.
XMLHelper.InsertSoapEnvelopeIntoWebRequest(soapEnvelopeXml, webRequest);
// attach the cert
if (this.chkSendClientCert.Checked)
{
X509Certificate myCert = X509Certificate.CreateFromCertFile(#"C:\temp\CDX-IHAT_DevClientCert.cer");
webRequest.ClientCertificates.Add(myCert);
}
else
{
webRequest.ClientCertificates.Clear();
}
// begin async call to web request.
IAsyncResult asyncResult = webRequest.BeginGetResponse(null, null);
To further complicate matters, the WCF Service that this applies to is a BizTalk service.
Here's how I ended up doing it.
The Server Config:
<customBinding>
<binding name="CustomCDARequestEndpointBinding">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="UserNameOverTransport" />
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
The Client Config:
<system.ServiceModel>
<bindings>
<customBindings>
<binding name="CustomBinding_ITwoWayAsync">
<security defaultAlgorithmSuite="Default"
authenticationMode="UserNameOverTransport"
requireDerivedKeys="true"
includeTimestamp="true"
messageSecurityVersion="WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
>
<localClientSettings detectReplays="false" />
<localServiceSettings detectReplays="false" />
</security>
<textMessageEncoding messageVersion="Soap11" />
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="ohBehave">
<clientCredentials useIdentityConfiguration="false">
<clientCertificate findValue="6D0DBF387484B25A16D0E3E53DBB178A366DA954" storeLocation="CurrentUser"
x509FindType="FindByThumbprint" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
<client>
<endpoint address="https://myservice/CDASubmitService/CDASubmit.svc"
binding="customBinding" bindingConfiguration="SubmitDev" behaviorConfiguration="ohBehave"
contract="CDASubmitService.CDASubmit" name="SubmitDev" />
</client>
</system.serviceModel>
The key to getting it working was the <httpsTransport requireClientCertificate="true" /> element and the <security authenticationMode="UserNameOverTransport" element/attribute.
This configuration allowed me to submit a message to a WCF (BizTalk) service completely through configuration files, with no changes to actual code. It still allows me to submit to it VIA WebRequest as well, as shown above.
I have to give credit to this post:
WCF Client Certificate AND UserName Credentials forbidden
as well as this one:
Translate non-BizTalk WCF config into BizTalk WCF-Custom endpoint
for finally getting me on the right track. I always shied away from Custom Bindings in WCF because I assumed it was overkill, but they are really nothing crazy, just a way to supply more detailed config than is available out of the box.

WCF client not mapping and returning null objects

I've setup a Service Reference (WCF Client) to call a Java Web Service from a Console Application I've setup for testing. It is using HTTPS. I have Fiddler setup and can see the proper values being sent and returned from the service (in Fiddler). But no matter what method I call, the returned values, regardless if it is a String or an object, comes back as Null.
I'm not sure if the proxy client mapping isn't working or if I need to change a configure value in app.config.
app.config:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<customBinding>
<binding name="ResultsSOAP12Binding">
<textMessageEncoding messageVersion="Soap12" />
</binding>
<binding name="ResultsSOAP12Binding1">
<textMessageEncoding messageVersion="Soap12" />
<httpsTransport />
</binding>
<binding name="ResultsSOAP12Binding2">
<textMessageEncoding messageVersion="Soap12" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="https://services.acme.com/results"
binding="customBinding" bindingConfiguration="ResultsSOAP12Binding1"
contract="ResultsServiceReference.Result
</client>
</system.serviceModel>
</configuration>
Code:
static void CallResults()
{
var resultsRequest = new ResultsServiceReference.ResultsRequest();
var client = new ResultsServiceReference.ResultsPortTypeClient("ResultsSOAP12BindingQSPort");
Console.WriteLine("Call Results Service");
ResultsServiceReference.ResultsBatch result = client.latestResults(resultsRequest);
Console.WriteLine(result.Status);
Console.ReadLine();
}
In this code the variable result is null, even though when you look in Fiddler you can see the XML. No error is displayed until you try to use result.
BTW, I tried setting a breakpoint inside the latestResults method in the proxy class reference.cs, but the debugger doesn't reach it.
You may want to configure and enable WCF diagnostic event tracing and message logging, then rerun the test transaction and review the service trace log file. The following link demonstrates how to enable tracing and message logging.
http://msdn.microsoft.com/en-us/library/ms751526.aspx
In our experience, service discrepancies which otherwise show no obvious error, often show up in the service trace file.
Note: The breakpoint inside the proxy class reference.cs may not be reached because the following attribute has been set:
[System.Diagnostics.DebuggerStepThroughAttribute()]
Most probably the WSDL has a mistake and the schema inside it does not match the actual response XML. You can publish the WSDL (and any referenced XSD) here together with the SOAP response (or mail them to me so I will try to look). Or you can set up a WCF service stub from the exact same WSDL (or from the client contract you already generated) and compare the response WCF sends to the one the actual service sends.
You want to look for differences in XML namespaces (and understand delicate parts like if this is default namespace or prefixed one) and in the name of the first element under the body.

Categories

Resources