WCF and Multiple Host Headers - c#

My employers website has multiple hostnames that all hit the same server and we just show different skins for branding purposes.
Unfortunately WCF doesn't seem to work well in this situation.
I've tried overriding the default host with a custom host factory.
That's not an acceptable solution because it needs to work from all hosts, not just 1.
I've also looked at this blog post but either I couldn't get it to work or it wasn't meant to solve my problem.
The error I'm getting is "This collection already contains an address with scheme http"
There's got to be a way to configure this, please help :)

If you don't put an address in the endpoint then it should resolve to whatever server hits the service. I use this code and it resolves both to my .local address and to my .com address from IIS.
<system.serviceModel>
<services>
<service name="ServiceName" behaviorConfiguration="ServiceName.Service1Behavior">
<endpoint address="" binding="wsHttpBinding" contract="iServiceName">
</endpoint>
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceName.Service1Behavior">
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>

I don't think that the host base addresses solution posted above will work for IIS-hosted websites (the OP did mention that this was for his employer's website).
See this blog post
Also, the other answer further up by thaBadDawg won't work where multiple host headers are specified - you'll just get the errors that the OP mentions ("This collection already contains an address with scheme http".)
I don't think any of the solutions mentioned so far will work, because it doesn't look like WCF allows a single service to be accessible for a single site with multiple host headers from all of the sites. The only workaround I could find for .Net 3.5 (and under) is to create a different contract for each of the host headers, and use the custom ServiceHostFactory to use the correct host header based on which contract is specified. This isn't at all practical. Apparently .Net 4.0 will resolve this issue.

I met this problem several days ago. Actually I have the same situation that Ryu described originally in his question. We have one virtual directory for many customers but each of them has his own binding. Like "http://company1.product.com", "http://company2.product.com" etc.
Solution described here works. But what is the price! We should change web.config each time when we need to add new binding. And also web.config should contain absolute path prefix
like <add prefix=”http://company1.product.com”/>.
It is possible to bypass first problem. I wrote my own CustomHostFactory for WCF service, where I dynamically add endpoints. And this endpoints I retrieve from IIS bindings(there is a way to get information from IIS).
Here is sample code :
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
var serviceHost = base.CreateServiceHost(serviceType, baseAddresses);
var webHttpBinding = new WebHttpBinding();
var serviceEndpoint1 = serviceHost.AddServiceEndpoint(typeof(IService), webHttpBinding,
"http://company2.product.com/WCFService/Service.svc");
var serviceEndpoint2 = serviceHost.AddServiceEndpoint(typeof(IService), webHttpBinding,
"http://company1.product.com/WCFService/Service.svc");
var webHttpBehavior = new WebHttpBehavior();
serviceEndpoint1.Behaviors.Add(webHttpBehavior);
serviceEndpoint2.Behaviors.Add(webHttpBehavior);
return serviceHost;
}
And instead of hardcoding endpoint urls, you sould retrieve them from IIS.
But ServiceHost is created once when application starts. So if you need to add new binding, you should restart IIS. It is not a solution for us.
That is why we decided to move to asmx (like is described here).
And wait until Framework 4.0 release, where multiple bindings should be supported.

A simple workaround that doesn't involve any code or config changes is to create yet another website in IIS which points to the same physical directory, and the same app pool, but with a different host-header binding. This can be done for as many different host names as you have.

I'm sure you've figured it out by now, but just for fun I'll post here anyway.
I've had this exact problem and spent forever trying to figure it out. The best solution is to put the host base addresses in your service definition, which allows the service to operate under these multiple addresses. For this solution to work, you still need the ServiceHostFactory to be overridden. Since you've already done that, just leave it in there.
<service behaviorConfiguration="ServiceBehaviour" name="Api.Service">
<endpoint address="soap" binding="basicHttpBinding" contract="Api.IService" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://host1.com/Api" />
<add baseAddress="http://host2.com/Api" />
</baseAddresses>
</host>
</service>

Related

WCF TCP Shared Services Enable Service Fails to Start

In short, when I go to start the worker process service process, it fails and returns:
The CS.Connector.Protean service on Local Computer started and then
stopped. Some services stop automatically if they are not in use by
other services or programs
I remove the attribute portSharingEnabled="true" from the service model in the config file and then the worker process service starts and executes as expected. I then add the attribute back into the config and the worker process services won't start again. I have include the service model config at the bottom of this post.
The Net.TCP Sharing Service is running, so it should be intercepting the incoming net.TCP connection.
I have read this MSDN article, but I must be missing something somewhere.
Could it be do doe with the mex end point not using a port sharing binding? I tried adding a binding to the mex end point, but still no joy. :.(
Help! Thanks
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="TCPSecure" portSharingEnabled="true">
<security mode="Message" />
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="Protean.Connector.ProteanConnector">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="TCPSecure" contract="Protean.Connector.IProteanConnector"></endpoint>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://10.1.2.124:60000/ProteanConnector" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
Update!
I started off with a working service and confirmed this by a test.
I added the attribute port sharing and the service failed to start.
I removed the mex end point and the service ran successfully. An application that had a established connection was able to use the service successfully.
I added the mex back in with a reference to a binding that only had port sharing enabled and the service fail to start.
I need the mex to make the service discoverable to the IDE Visual Studios.
I suppose that the question now is, how does the end point, for the metadata exchange, work along with the port sharing attribute?
Update 2
MSDN Social Webpage Link
This forum tells me to change the mex binding from mexTcpBinding to netTcpBinging. I did and the service ran, but I am unable to discover the service now in the IDE.
Is this the correct solution path? The journey continues.
I'm starting to think the deeper I dig the more likely it will be that I stumble upon some government conspiracy. #HumourInDarkTimes #StrangerThings ;)
Solution
var type = typeof(LoggingManager);
using (var host = new ServiceHost(type))
{
/*
* Work-around to the conflict between global port sharing and local port sharing
* caused by Net.Tcp Sharing Serivces and the default port exclusivity (hogging) of Metadata Exchange binding.
*/
var mexBinding = new CustomBinding(MetadataExchangeBindings.CreateMexTcpBinding());
mexBinding.Elements.Find<TcpTransportBindingElement>().PortSharingEnabled = true;
host.AddServiceEndpoint(typeof(IMetadataExchange), mexBinding, "mex");
host.Open();
Console.WriteLine("Service started. Press [Enter] to exit.");
Console.ReadLine();
host.Close();
}
Description of Solution
I read a number of articles that suggests a solution through swapping the binding mexTcpBinding for netTCPBinding, I found that this did not work in my instance.
The above solution gets the mexTCPBinding and adds to it the attribute PortSharingEnabled = true. This is used in creating a new endpoint code-matically and passed to the service host. Finally, I have removed the meta-exchange end point from the config and, hey-presto, it worked.
Useful Links
Article explaining fix
Similar question

IIS hosted wcf returns me blank page

I have a wcf application. It has "Service1.svc" file. In the web.config file I specified
http://localhost:2005/EmployeeService.svc
as an endpoint. When clicking browse from Visual Studio there is no problem. But, when I hosted it on IIS server I get a blank page. The interesting thing is, If I remove the address from the web. config this time I can see the service at this address.
http://localhost:2005/EmployeeService.svc
web.config file is as below:
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior name="EmployeeServiceBehaviour">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<services>
<service behaviorConfiguration="EmployeeServiceBehaviour" name="EmployeeConfiguration">
<endpoint address="http://localhost:2005/EmployeeService.svc" binding="basicHttpBinding"
bindingConfiguration="" contract="IEmployeeConfiguration" />
</service>
</services>
</system.serviceModel>
<system.web>
<compilation debug="true"/>
</system.web>
<system.webServer>
<directoryBrowse enabled="true"/>
</system.webServer>
</configuration>
Could you please explain, why I get a blank page on IIS when I provide the adress.
This may happen in HTTP Activation is not installed in Programs and Features in your Windows installation. Check HTTP Activation for both .Net 3.5 and 4.6 to enable opening WCF service via HTTP request to .svc file hosted in IIS.
I think you can remove all text on the address before Service.svc. This might be an issue with different urls.
When you specify the address part, you tell WCF the service is only available on that address. When going through the endpoints, WCF will not find one that matches the given URL and bail out.
When you have multiple endpoints you need the address field. Otherwise you don't.
Read http://msdn.microsoft.com/en-us/library/ms733749(v=vs.110).aspx
And in particular this part:
When hosting with IIS, you do not manage the ServiceHost instance yourself. The base address is always the address specified in the .svc file for the service when hosting in IIS. So you must use relative endpoint addresses for IIS-hosted service endpoints. Supplying a fully-qualified endpoint address can lead to errors in the deployment of the service.
Check if Service attribute in SVC markup equals your implementation class.
Check endpoint contract name. It has to be exactly full name of your interface. Don't add assembly name, it won't work.
Check <serviceMetadata httpGetEnabled="true" />. If false, you get blank page too.
And 4th change which helped me (but I have no idea why): Remove behavior name <behavior name=""> and remove behaviorConfiguration attribute from service element. It becomes default so the behavior will be used anyway.
Be sure you made an Application Pool with active user like this: https://stackoverflow.com/a/19654633/2148387
Be sure you created Web Application where your WCF is hosted
Check if AppPool and WebApp are running
Enable Directory Browse in your Web.config like here: https://stackoverflow.com/a/19630263/2148387

WCF endpoint configuration: 2nd endpoint doesn't work at all, 1st endpoint doesn't seem affected by binding

In a WCF service hosted in IIS, I'm trying to set up multiple endpoints. One for SOAP and one for SOAP12. Per the MSDN documentation, I've edited Web.config like:
<services>
<service name="MyNamespace.MyClass">
<endpoint address="" binding="basicHttpBinding" contract="IContract" />
<endpoint address="Endpoint2" binding="wsHttpBinding" contract="IContract" />
</service>
</services>
This doesn't seem to have any effect. There is no answer on URL:
http://localhost:51454/MyClass.svc/Endpoint2
If I change IContract to IContract2, I get the error:
The service '/MyClass.svc' cannot be activated due to an exception during
compilation.
So the Web.config I'm editing is the one being used.
Changing the binding for the default address from basicHttpBinding to wsHttpBinding doesn't have any effect. The WSDL stays the same.
The WSDL includes this bit, which seems to suggest that it's running using a generated binding:
<wsdl:service name="TapasSim">
<wsdl:port name="BasicHttpBinding_IContract"
binding="i0:BasicHttpBinding_IContract">
<soap:address location="http://localhost:51454/MyClass.svc"/>
</wsdl:port>
</wsdl:service>
Why does the WCF service not use the configuration from Web.config?
Why does WCF not listen on /Endpoint2 with the SOAP12 binding?
Why does the default endpoint not change from basicHttpBinding to wsHttpBinding?
Try adling a base adress for the endpoint:
<service name="namespace.Service">
<host>
<baseAddresses>
<add baseAddress="http://localhost:51454/myclass.svc"/>
</baseAddresses>
</host>
// endpoint omnited
Or try adding a slash before the address:
endpoint address="/Endpoint2" binding="wsHttpBinding" contract="IContract"
The MSDN article is correct and there is nothing wrong in your configuration. I created a WCF client using VS and was able to successfully call using
http://localhost:51454/MyClass.svc/Endpoint2
Apparently, it does not appear as a valid url from browser. try consuming from a client using the second url and it should work
The problem was the service name:
<service name="MyNamespace.MyClass">
The class name was wrong. When you enter a wrong contract interface, WCF throws an error. But a wrong class name is silently ignored. That explains why it fell back on the default configuration.

Windows Service Hosted TCP WCF Service

I am trying to host a WCF service on a windows 2008 R2 server as a windows service. I followed the directions provided by msdn (found here). Everything works fine as long as everything is part of the same solution in visual studio. However, I tried creating a client in a different solution (on the same machine) and it can't find the service. I get an 'Add Service Reference Error' shown below.
My goal is to be able to access the wcf service remotely, but I can't seem to even access it locally unless the client was created within the same client. Is there any guides, tutorials, or helpful hints that anyone can give me to get this to work?
Update:
It seems that even though the windows service is running, the WCF service doesn't seem to be listening to any ports. Which would suggest that it isn't running. This also explains why everyone's first thought I didn't have the service running. I had assumed that since the windows service was running and that the same solution client worked, that the WCF service was working as well. Turns out that Visual Studio was starting up a WCF service whenever I ran the same solution client.
So, why isn't the windows service starting the WCF service? Any ideas?
It turns out that there was a problem with the tutorial provided by MSDN (provided in the question above). They named both the windows service and the WCF service Service1 which is the default name for both of them.
The windows service was suppose to start the WCF service, however, it was actually trying to start itself again because both services had the same name.
myServiceHost = new ServiceHost(typeof(Service1));
To fix this problem, you can rename one of the services or fully qualify the WCF service when referenced in the windows service.
myServiceHost = new ServiceHost(typeof(WcfServiceLibrary1.Service1));
The funny thing is that the code provided still looks like it works because Visual Studio is smart enough to notice that the WCF service isn't running and it starts an instance up behind the scenes.
It was a simple bug to fix, but because Visual Studio was hiding the problem from me, I don't think I would have found it without the help from Espen Burud.
There are two ways for Add Service Reference to learn about a service:
Discover button: searches the projects in the current solution.
Go button: connects to the service in the Address box and retrieves the metadata.
You need to actually have the service running before you click Go.
EDIT
I just noticed from your screenshot that you're trying to connect to a net.tcp URL. I think it's more common to use http for MEX. Your app.config would look something like:
<services>
<service behaviorConfiguration="WcfServiceLibrary1.Service1Behavior"
name="WcfServiceLibrary1.Service1">
<endpoint address="" binding="netTcpBinding" bindingConfiguration=""
contract="WcfServiceLibrary1.IService1">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
<endpoint address="mex" binding="mexTcpBinding" bindingConfiguration=""
contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8523/Service1" />
<add baseAddress="http://localhost:8524/Service1" />
</baseAddresses>
</host>
</service>
</services>
Note the different port number for the http base address. You would then use "http://localhost:8524/Service1" in the Add Service Reference tool. You should also be able to connect to it with your web browser.
To allow metadata exchange via http GET (e.g. from a browser), you also need to enable it via a behavior:
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled="True"/>
</behavior>
</serviceBehaviors>
</behaviors>
I'm not sure if the Add Service Reference tool cares about that.
Even if you don't want to allow http get access (httpGetEnabled="False"), you still need to include this behavior to enable MEX (unless you're adding it programatically).
I have tested the MSDN article in the and it works without modifications. If the firewall is enabled on the server, I think you will need to add some rules for your service.
To verify that the service are listening on the correct tcp port, you can use command: netstat -a. If the service are listening on the correct port, this command will return:
Proto Local Address Foreign Address State
TCP 0.0.0.0:8523 machinename:0 LISTENING
I managed to figure out the issue. My service didn't know about the endpoints because I hadn't copied the service configuration from the app.config in the WCF project into the app.config of the actual windows service. Once I did that it functioned correctly.
This was not made clear in the original MSDN article which I had also followed although it is mentioned in a comment in the WCF app.config.

Minimum configuration to make "Add Service Reference > Discover" work

I have already a few WCF + Windows Service tested and working correctly as run as a Windows Service on remote dev machines. Except for one.
In order to debug, I tried to host the WCF (without the Windows Service) using the built-in "Add Service Reference" and then hosting it in visual studio (don't know what the host is called).
Anyways, I cannot get the Add Service Reference to Discover my services.
Since I am using Windoes Services, I am using TCP stuff. This are some things I have done, all in the winforms app that I am adding the service reference:
Add Project > Properties > Debug > Command line arguments: /client:"WcfTestClient.exe" but the exe doesn't run.
And my App.Config
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<client>
<endpoint address="net.tcp://localhost:32279/SYNC" binding="netTcpBinding"
bindingConfiguration="tcpSyncBindingConfig" contract="Company.Data.Sync.ILocalCacheSyncContract"
name="tcpSyncClientEP" />
</client>
<bindings>
<netTcpBinding>
<binding name="tcpSyncBindingConfig" maxReceivedMessageSize="6553600" />
</netTcpBinding>
<mexTcpBinding>
<binding name="tcpMexBindingConfig" />
</mexTcpBinding>
</bindings>
<services>
<service behaviorConfiguration="svcBehavior" name="Company.Data.Sync.Services.LocalCacheSyncService">
<endpoint address="net.tcp://localhost:32279/Sync" binding="netTcpBinding"
bindingConfiguration="tcpSyncBindingConfig" name="tcpSyncListenEP"
contract="Company.Data.Sync.Services.ILocalCacheSyncContract" />
<endpoint address="net.tcp://localhost:32279/Sync/mex" binding="mexTcpBinding"
bindingConfiguration="tcpMexBindingConfig" name="tcpMexEP" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="svcBehavior">
<serviceMetadata httpGetEnabled="false" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Side note: the ServiceOperation are returning Microsoft.Synchronization.Data.xxx sutff for example SyncContext. I might have to add a reference in the Winforms? Or will the "Add Service Reference" add them for me? The service and contracts are generated by the Local Database Cache template. perhaps not related to the problem.
After posting, I continued to look for answers and this is what I experimented to be working:
The project file itself e.g. *.csproj for C# project must contain the ProjectTypeGuid for WCF for Visual Studio to even start checking the project for Services.
<ProjectTypeGuids>{3D9AD99F-2412-4246-B90B-4EAA41C64699};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
More GUIDs can be found at http://www.mztools.com/articles/2008/mz2008017.aspx. (Thanks mztools!)
The WCF project can have multiple services, and each of them needs to to have their config defined in the project's App.Config. Each service needs to have a different mex endpoint address. The services can also share a single servicebehavior which has the serviceMetadata extension defined. Mex endpoint does not need behaviors or binding config. But remember to set mex endpoint contract to IMetadataExhchange.
For TCP mex, the serviceMetadata > HttpGetEnabled must be set to false.
I believe this is the minimum settings.
Right click on the service you wish to host on your PC, then right click and go Debug > Start New Instance
Then go your other application (I assume same solution) and copy the URL it is running at.
Then you can start that one with the same method above and debug both projects on your local machine.
I assume this is what you are trying to do. Please let me know if it was something else.

Categories

Resources