I have inherited a Silverlight application that consumes a WCF service, and is hosted in an ASP.NET web form. The application needs to run over HTTP and HTTPS, and will be installed on a customer's own server. The client code was originally generated using the Add Service Reference pointing to the locally-hosted service, so my ServicesReferences.ClientConfig obviously contains hard-coded references to localhost - not much use when deploying to another server, so obviously I need to be able to programmatically set the endpoint address that the client uses.
My code is currently:
var binding = new BasicHttpBinding
{
MaxBufferSize = 2147483647,
MaxReceivedMessageSize = 2147483647,
};
binding.Security.Mode = HtmlPage.Document.DocumentUri.Scheme.StartsWith("https")
? BasicHttpSecurityMode.Transport
: BasicHttpSecurityMode.None;
var documentUri = HtmlPage.Document.DocumentUri;
var builder = new UriBuilder(
documentUri.Scheme,
documentUri.Host,
documentUri.Port,
documentUri.LocalPath.Replace("hostpage.aspx", "MyService.svc"));
var client = new CustomerDetailServicesClient(binding, new EndpointAddress(builder.Uri));
client.ChannelFactory.Faulted += OnChannelFactoryFaulted;
client.DoSomething();
and while this works OK when I use HTTP, attempts to access via HTTPS fail with 404s for each call to the WCF service. Using Fiddler, I can see that it is only the URI scheme that is changing, and if I enter the HTTPS address of the service, I get the expected metadata page.
Is there something obvious that I am missing?
sigh slap me with a wet herring. The HTTPS bindings for the web service had been commented out in web.config (since not all our customers can/want to run HTTPS on their servers, and having WCF bindings for HTTPS when IIS is not configured for HTTPS breaks WCF in lots of noisy ways).
It's amazing what fresh perspective and clarity a good night's sleep can bring to a problem.
Related
I am new to WCF development and I am trying to create a WCF service hosted in a console app.
I have already created the WCF service and tested it by running it on IIS Express. Doing so, the WCF service will be accessible from http://localhost:5576/MyFirstService.svc. Within the service, I have defined a GET endpoint /test/<param> just to test if it works. Upon visiting the url with Postman http://localhost:5576/MyFirstService.svc/test/123, it will echo back 123.
My console app that hosts the WCF on the other hand is super simple. I followed the tutorial (http://www.topwcftutorials.net/2014/05/wcf-self-hosting-console-application.html). The relevant code is below:
Uri httpBaseAddress = new Uri("http://localhost:4321/StudentService");
//Instantiate ServiceHost
studentServiceHost = new ServiceHost(typeof(StudentService.StudentService), httpBaseAddress);
//Add Endpoint to Host
studentServiceHost.AddServiceEndpoint(typeof(StudentService.IStudentService), new WSHttpBinding(), "");
//Metadata Exchange
ServiceMetadataBehavior serviceBehavior = new ServiceMetadataBehavior();
serviceBehavior.HttpGetEnabled = true;
studentServiceHost.Description.Behaviors.Add(serviceBehavior);
//Open
studentServiceHost.Open();
Console.WriteLine("Service is live now at : {0}", httpBaseAddress);
Console.ReadKey();
As I launched the console app and visited http://localhost:4321/StudentService, I am greeted with the standard page talking about wsdl. However, if I tried to visit http://localhost:4321/StudentService/test/123, I get a 400 bad request error.
Am I doing things right? What is the path that I should be using to get to my endpoints? I tried many variations of the URL and it just does not seem to work.
Your first service, http://localhost:5576/MyFirstService.svc, was running within the context of IIS (yes even though it's express) so that is what allows for the URL (REST style) routing like you were seeing in your "test/123" example.
But the code example, and posted reference link, is actually a self-hosted service from a console application which doesn't utilize IIS or WAS (Windows Activation Service) so routing won't be available.
Don't get me wrong, your StudentService will still work just fine if called via SOAP, just not from a REST perspective which is what Postman is used for.
There are free tools out there like SoapUI that work just like Postman to test your WCF services.
I have a SOAP service I need to talk to from a C# application. I have a PHP test application that is using the same SOAP service today, using the standard SoapClient.
It is used similar to this:
$options = array(
'login' => $username,
'password' => $password,
'location' => "https://$serveruri/soap",
);
$service = new SOAPClient($wsdl, $options);
$retval = $service->SomeMethod($parameter);
And this works just fine. As far as I understand (I am useless at PHP), since we're not setting an authentication option, it should go with Basic authentication.
I am trying to talk to the same endpoint in C#, and it keeps prompting me for authentication. I am not sure how to recreate the same authentication. I believe some of the problems come from using https - I have been able to talk to a similar system in the past using http and the BasicHttpBinding, but since I have to talk to this one across https, that is no good.
So I have generated a client proxy using SvcUtil, and I am trying to talk to it. Here is my current iteration of desperation:
var endpoint = new EndpointAddress(SoapUrl);
var binding = new WSHttpBinding(SecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
binding.SendTimeout = new TimeSpan(0, 0, 10);
ServicePointManager.ServerCertificateValidationCallback +=
(sender, cert, chain, sslPolicyErrors) => true;
var client = new soapServiceClient(binding, endpoint);
if (client.ClientCredentials != null)
{
client.ClientCredentials.UserName.UserName = Settings.AuthenticationUsername;
client.ClientCredentials.UserName.Password = Settings.AuthenticationPassword;
}
var retval = client.SomeMethod(parameter);
And to address Jons comment below: client.ClientCredentials really is not null, ever, when I run this. ReSharper put that there to stop itself from nagging.
When I run this, I get a 401 Unauthorized from the server. I get the following in Visual Studio 2013:
Additional information: The HTTP request is unauthorized with client authentication scheme 'Basic'. The authentication header received from the server was 'Basic realm="bla bla service"'.
How can I get WCF to behave like PHPs SOAPClient in this case?
Is there another binding I should use? I have tried different SecurityMode values, I have tried Digest authentication, but they get me nowhere. Since this happens over https I guess I can't Wireshark it either..
Thankful for any insights!
EDIT: Jon suggested to give Fiddler2 a try. I turned on https decryption and gave it a go.
Two things jump out at me:
1) When I run the PHP script locally (using EasyPHP running on my machine), it contacts the SOAP server and gets data. However, Fiddler2 does not see this traffic in any way.
2) When I run my app in Visual Studio 2013, Fiddler2 does see the traffic, and it decrypts it. I see two attempts to contact the SOAP endpoint; Both get a 401 reply. The first contains no auth information (looking at the Inspectors -> Auth part of Fiddler2) and just says:
No Proxy-Authorization Header is present.
No Authorization Header is present.
Then the second request tries to fix that with an Authorization header that looks kinda like this:
Authorization Header is present: Basic [some hash data]
Decoded Username:Password= [correct username]:[correct password]
But as mentioned - this still provokes a 401 from the other side.
I have no idea why the PHP traffic doesn't show up in Fiddler2, but it very clearly receives live data from the other side. Perhaps PHP doesn't use the network stack in a way that Fiddler2 can pick up, I have no idea.
EDIT 2: After a bunch of filthy debugging on the PHP side, I finally got to compare the request/response cycle of the PHP app with the WCF one. That got me a lot closer (somehow I had gotten a sub character into the auth username, which caused the authentication issue), but now I am struggling with a ProtocolException, caused by my binding being set to Content-Type application/soap+xml, and the external service (I believe this to be Linux based) returning text/xml.
My understanding from abusing Google on this is that text/xml is common for SOAP 1.1, while application/soap+xml is what is used with SOAP 1.2.
Also, I understand that in WCF BasicHttpBinding supports SOAP 1.1, while WsHttpBinding supports SOAP 1.2.
Since this service is on the public internet, it requires https for security. I believe that the original SOAP service on the system itself uses HTTP, but it is behind a gateway that requires HTTPS and then passes it on as HTTP to the actual box serving the requests.
The core of the question then becomes: Is there a way for me to access a SOAP 1.1 service using HTTPS with WCF?
So I've come full circle on this. I started out this morning as a WCF newbie, and while I won't even claim to be a competent user yet, I understand a lot more about the bindings.
By using shotgun debugging I had actually done it correctly a few times, but I was tricked by an invisible sub character (^Z I believe) in the username I copied into my code.
Also, this was compounded by me seeing a lot of information about BasicHttpBinding not supporting https - it does, if you create it like this:
var binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
Credit to this relatively old article for educating me to the point of figuring it out:
https://msdn.microsoft.com/en-us/magazine/cc163394.aspx
From this article I also came to the understanding that had BasicHttpBinding not supported https, I could have built my own CustomBinding that worked the same with relatively little effort.
Also thanks to our overlord Jon Skeet for pushing me in the right direction of figuring out what was actually being sent across the wire!
By default AppFabric adds WorkflowControllEndpoit with NetNamedPipeBinding.
In my environment I need to manage workflows from another server which means that I cannot use net.pipe.
How do I add a WorkflowControllEndpoint on HttpBinding?
Please note that I allow users to dynamicly add workflows so I'm looking for a way to add endpoint to all workflows hosted by AppFabric. tag in web.config is not good for me.
Attempt 1:
Using behaviour to add endpoint. I've had problems adding WorkflowControllEndpoint. I've add following to behavior
var host = (WorkflowServiceHost)serviceHostBase;
host.AddServiceEndpoint(new WorkflowControlEndpoint(new
WSHttpBinding(), new EndpointAddress(host.BaseAddresses[0] +
"/Control")));
that compiles and runs but when I make request to this endpoint I'm getting
Content Type application/soap+xml; charset=utf-8 was not supported by service http://localhost/WorkflowServices/xx.xamlx/Control. The client and service bindings may be mismatched.
I guess that's because endpoint wasn't added and it goes to a default endpoint which doesn't accept Soap
Attempt 2
Disabling net.pipe - That forces AppFabric to bind to WSHttpBinding for WorkflowControllEndpoint but then I get wornings in Persistence window like
workflow persistence is not fully functional because the net.pipe
What functionality do I loose ?
I want to be able to consume a WCF Service endpoint in my Windows Phone 8 app.
Searching on Google only showed me that I had to Right-Click on the WP8 Project, select 'Add Service Reference'... Which is not a viable solution in my case.
I want to be able to consume a WCF service inside my Windows Phone 8 app, programmatically.
Where do I define my client endpoint certificate in a Windows Phone 8 app?
Imagine that I want to make a Windows Phone 8 app, which should be able to connect to a WCF service hosted on another device, i.e. a computer. Then the WP user needs to enter the hostname of that computer in order to be able to connect to the WCF service.
I advice you to use "Add Service Reference" to generate the proxy class.
The DTO and Client proxy will be automatically generated. You will benefit from a huge boost in productivity, type safety and name checking.
Then you can specify the url at runtime using the appropriate constructor. For instance :
private MyServiceClient GetMyServiceClient(string url)
{
Uri uri = new Uri(url);
BasicHttpBinding binding = new BasicHttpBinding(BasicHttpSecurityMode.None);
EndpointAddress address = new EndpointAddress(uri);
MyServiceClient client = new MyServiceClient(binding, address);
return client;
}
(MySericeClient being the generated proxy)
The .config stuff is optional, you can remove it.
When you add a Service Reference, your WP8 Project is auto generating a proxy class that wraps the WCF Service. Then your code uses this proxy class.
The other way of doing this is creating the proxy class manually, and believe me, you want to avoid this if you can...
Proxy Client class generated by Add Service Reference will use hostname (endpoint address) from config only when you use its parameterless constructor. You can specify endpoint adress manually at runtime of course.
You can create service contract portable class library and share it between client and server. Then you dont have to generate proxy classes, but you use ChannelFactory API: http://www.c-sharpcorner.com/UploadFile/ff2f08/channel-factory-in-wcf/
I'm trying to put a number of features together, which is proving increasingly difficult due to the limitations of the .NET Compact Framework.
Specifically, I've got a WCF service and I'm writing a mobile device client for it. The catch? I want to use some sort of data compression (due to a very slow modem connected to said device) and HTTP DIGEST authentication (which is already in place on the site hosting the WCF service).
I've followed this blog entry to get the compression and generated code needed for the WCF service client.
I am, however, struggling with the HTTP DIGEST. I've no idea how to add this functionality.
Previously I didn't use compression and as such I connected to the WCF service using SOAP, using a simple WebService reference, and to add HTTP DIGEST I had to override the GetWebRequest method and add the required headers manually. This time around the generated classes seem to give very little leeway and there isn't much I can override. Also, all security or authentication parameters seem to be designed for SSL, rather than such basic authentication schemes.
To summarize: how can I create a WCF client using compression and HTTP DIGEST authentication using .NET Compact Framework?
EDIT:
Here's the code I've currently got:
System.ServiceModel.Channels.CustomBinding customBinding = new System.ServiceModel.Channels.CustomBinding();
CompressionMessageEncodingBindingElement compressionBindingElement = new CompressionMessageEncodingBindingElement();
customBinding.Elements.Add(compressionBindingElement);
HttpTransportBindingElement httpBindingElement = new HttpTransportBindingElement();
customBinding.Elements.Add(httpBindingElement);
EndpointAddress endPoint = new EndpointAddress("http://localhost:5100/Service.svc");
ServiceClient client = new ServiceClient(customBinding, endPoint);
I suspect I somehow need to add the HTTP DIGEST functionality to the CustomBinding class, but I don't know how.
I suspect I should also note, that while I am using .NET Compact Framework 3.5, I am creating a Windows CE application. As such, I didn't bother downloading Windows Mobile 6 SDKs. If these SDKs add more functionality which can be used in Window CE applications and are required for the HTTP DIGEST to work, please let me know.
We ended up disabling the DIGEST authentication for devices running .NET CF. It's not as safe, but we figured the data send and retrieved by the devices running .NET CF in our case isn't THAT sensitive, so all we really need to do is validate it.
If the client is running on the .NET Compact Framework 3.5, you can use WCF to invoke the service and use the built-in support for HTTP Digest authentication without requiring SSL.
Here's how to programmatically configure a WCF client to use Digest authentication with the BasicHttpBinding:
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Digest;
var endpoint = new EndpointAddress("http://server/myservice");
var client = new MyServiceClient(binding, endpoint);
// We have to set the actual credentials on the client proxy object
// before invoking the service:
client.ClientCredentials.HttpDigest.ClientCredential.UserName = "me";
client.ClientCredentials.HttpDigest.ClientCredential.Password = "password";
try
{
client.MyServiceOperation();
client.Close();
}
catch
{
client.Abort();
}
Related resources:
The WCF subset supported by the .NET Compact Framework 3.5
WCF Guidance for Mobile Developers (see page 66 for HTTP Digest Authentication)
The only way to achieve this is to use HttpWebRequest (manually) and specify ClientCredentials, instead of the generated classes from NetCFSvcUtil which does not support authentication.
The only WS-Security specification it supports on CF with WCF is to effectively use message security with a Mutual Certificate exchange. (Which by the way has a memory leak which a colleage and I found: http://connect.microsoft.com/VisualStudio/feedback/details/727247/native-memory-leak-in-wcf-proxy-client-with-mutual-certificate-security-in-net-compact-framework-3-5-on-windows-ce-6-0)
Of note, the generated CFClientBase also has a memory leak which can be worked around, see: http://geekswithblogs.net/GruffCode/archive/2013/03/31/memory-leak-in-cfclientbaselttgt-service-proxy-for-compact-framework-.net.aspx
For reference: The WCF subset supported by NetCF: http://blogs.msdn.com/b/andrewarnottms/archive/2007/08/21/the-wcf-subset-supported-by-netcf.aspx