28

I have a simple WCF Web service. It's hosted on IIS under the default website in our production domain. (local address: 10.10.20.100)

By default this default website was setup for "All Unassigned" IP's on Port 80: however, I noticed that this caused the WCF Service to generate it's WSDL using the servers local DNS name. i.e. all the URIs in the wsdl were

http://myserver.subdomain.domain.com/.../...

This was no good as I need to expose this service to sites who have no knowledge of the production environments internal DNS. And this particular server doesn't have an external DNS Name. Just an external IP Address...

I've had some success with changing the Setting in IIS from "All Unassigned" -> "10.10.20.100"

This causes the Service to generate it's WSDL with the URIs

http://10.10.20.100/.../...

This is fine for other machines within the subdomain and on other subdomains but it's here that I get stuck. The servers External IP Address (1.2.3.4) is mapped through via some NAT/PAT translation so it isn't explicitly setup in the servers IP Settings (i.e. it doesn't show under IP Config)

So if I change the IIS Default Website IP Address from "All Unassigned" -> "1.2.3.4" as I did for the internal address, then the WCF Service just comes back with...

Bad Request (Invalid Hostname)

And if I leave IIS Configured on the Internal IP Address, and try to access the service via the external IP Address I get

No protocol binding matches the given address 
'http://1.2.3.4/TestService/Service.svc'. Protocol bindings are 
configured at the Site level in IIS or WAS configuration

Is there any way to make IIS/WCF generate it's WSDL URI's with an external IP Address that isn't explicitly configured on the server ?

Someone help me please before I dropkick WCF Services out the window.

Eoin Campbell
  • 43,500
  • 17
  • 101
  • 157
  • 2
    This is a good question. Why is it that questions like this get no upvotes, but questions like "Tired of programming GUI/Business Applications, what to do next?" get 4 in a matter of minutes? – Rob Apr 29 '09 at 20:04
  • I sadly have no answer for you, though - I can only offer an upvote. +1. – Rob Apr 29 '09 at 20:05
  • Appreciate the answers so far guys. I'm gonna try Steve's suggestion today by overriding the ServiceHost and if that doesn't work I'll bite the bullet and get sysops to get us a new FQDN for our API Server. – Eoin Campbell Apr 30 '09 at 08:01
  • this is the solution to resolve this problem http://stackoverflow.com/a/9671430 –  Feb 06 '13 at 14:17

6 Answers6

8

It's because you don't have your host headers set. This seems to be an extremely common problem, I run into it all the time. There's no configuration for the uris that it generates, it looks up the right address by examining the host header of the site. Even if it's in a virtual directory, you need to go to the parent, in your case, default directory, and add a host header.

Let me know if you don't know how to do this.

Joshua Belden
  • 10,273
  • 8
  • 40
  • 56
  • I had the same problem and it was fixed with this solution, but I *thought* it only effected https hostings. Maybe not. I added the host header using the adsutil.vbs script on the IIS server. It forces the 443 port to have a specific host name rather than the default. – Andy McCluggage May 01 '09 at 14:13
  • But I know you can set host headers for unsecure bindings, so maybe worth a try. – Andy McCluggage May 01 '09 at 14:16
  • how do you add a host header ? – Omu May 05 '10 at 06:48
  • 1
    http://technet.microsoft.com/en-us/library/cc753195%28v=ws.10%29.aspx provides direction on setting the host header – DougWebb Jul 14 '11 at 18:17
1

The problem is that after do a change on port or ip address you shold restart the application pool you using, because it still having the old information until it is recicled.

SandroG
  • 121
  • 8
1

Here's how you change the header in IIS7. Also put my web.config for some if it helps.

http://www.sslshopper.com/article-ssl-host-headers-in-iis-7.html

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />      
    <customErrors mode="Off"></customErrors>
  </system.web> 
  <system.serviceModel>
    <client/>
    <services>
      <service name="WcfService1.Service1"
behaviorConfiguration="MyServiceTypeBehaviors">
        <host>
        <baseAddresses>
          <add baseAddress="https://pws.sjukra.is/"/>        
        </baseAddresses>
        </host>
        <endpoint address="https://pws.sjukra.is/Service1.svc"
                  listenUri="/" 
        binding="wsHttpBinding"
        contract="WcfService1.IService1"
        bindingConfiguration="myBasicHttpBindingConfig"/>
        <endpoint contract="IMetadataExchange"
   binding="mexHttpsBinding"                   
   address="mex"/>
      </service>
    </services>
    <bindings>
      <wsHttpBinding>
        <binding name="myBasicHttpBindingConfig">
          <security mode="TransportWithMessageCredential">
            <transport clientCredentialType="Windows" />
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="MyServiceTypeBehaviors">
          <serviceMetadata httpsGetEnabled="true"/>
          <serviceDebug httpsHelpPageEnabled="true" includeExceptionDetailInFaults="true"/>
          <serviceCredentials type="System.ServiceModel.Description.ServiceCredentials">
            <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfService1.Service1,WcfService1"/>
            <serviceCertificate findValue="pws.sjukra.is" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
              <clientCertificate>
                  <authentication certificateValidationMode="ChainTrust" revocationMode="NoCheck"/>
              </clientCertificate>            
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true"></serviceHostingEnvironment>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>
McDowell
  • 107,573
  • 31
  • 204
  • 267
Vilhelm
  • 187
  • 2
  • 9
1

Even though this is an old thread it actually helped me migrate a project from VS Web Developer Express to MonoDevelop which included a WCF service.

The web application requested the JavaScript interface defined by the WCF service using the following URL: http://127.0.0.1:8080/path-to-service/service.svc/js, which gave me the error: No protocol binding matches the given address

Inspired by this thread I was able to fix the problem because accessing the service with localhost instead of 127.0.0.1 worked! With a request to http://localhost:8080/path-to-service/service.svc/js I got the JavaScript interface.

Actually my application didn't use an absolute URL to include the JavaScript interface, however by default when starting the application from MonoDevelop it would access the application using 127.0.0.1, thus the call to include JavaScript from the service failed.

It's still not perfect since I haven't been able to start the application from MonoDevelop using localhost instead of 127.0.0.1, since the configuration for XSP only allows me to specify an IP address, but at least I know how to get around it.

Thomas Bindzus
  • 1,508
  • 12
  • 12
1

I might be able to prevent Ninja violence... if I understand your problem ... You can manually specify the complete address the service should use in the web.config as opposed to having the ServiceHost figure it out for you. You have to set the base address of your service:

 <service behaviorConfiguration="Behaviour1" name="Api.Poll">
    <endpoint address="soap" binding="basicHttpBinding" bindingConfiguration="soapBinding"
      contract="Api.IPoll" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    <host>
      <baseAddresses>
        <add baseAddress="http://www.mydomain.com/Api" />
        <add baseAddress="http://10.10.20.30/Api" />
      </baseAddresses>
    </host>
  </service>

Using this method, your service should accept the base address specified, plus the service name, with the additional endpoint address if you have one. Also, you would need to use a custom ServiceHostFactory to set the base address programmatically. See below:

    public class ServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {                        
        ServiceHost host;

        host = new ServiceHost(serviceType, baseAddresses[0]);

        return host;
    }

Lastly, once you've build the ServiceHostFactory class, you have to hook it up to your service by editing the markup in the .svc file:

<%@ ServiceHost Language="C#" Debug="true" Service="Api.Poll" Factory="Api.ServiceHostFactory" CodeBehind="Poll.svc.cs" %>
Steve
  • 939
  • 1
  • 9
  • 18
  • The BaseAddress section of the web.config is completely ignored when hosted under IIS. Makes no difference having it present or not. IIS figures out the address based on the Virtual Directory configuration. http://stackoverflow.com/questions/56249/wcf-service-configuration-file-question-regarding-baseaddresses – Eoin Campbell Apr 29 '09 at 19:10
  • 1
    Hmm... I've used it before under IIS, but that was a different scenario with multiple base addresses. I wonder if you could use a ServiceHostFactory to set the base address programmatically. See revised answer. – Steve Apr 29 '09 at 19:41
  • Cheers for this Steve... Its 9pm GMT here so I'll give this a try when I get back to the office and come back to you tomorrow. Can you just clarify where I create this class. Do I just drop it into the App_Code folder and then have my Service Extend this ? – Eoin Campbell Apr 29 '09 at 20:17
  • Yeah, you could just put it in the App_Code section, that would work. There's one last step to wire it up though, to do that you have to modify the markup of the .svc file. I've updated the answer to include that. – Steve May 01 '09 at 16:51
1

Must it be an IP address and not an FQDN? By swapping to an FQDN and setting that in the host headers for the site, then binding to it via

cscript //nologo %systemdrive%\inetpub\adminscripts\adsutil.vbs set W3SVC/1/ServerBindings ":80:hostname.example.com"

then recycling the app pool will then produce that host name in the generated WSDL. You get benefits with that - you can setup an internal DNS which resolves that FQDN to the internal IP, and an external DNS which resolves to your firewall IP, then the same system will work without any changes.

blowdart
  • 55,577
  • 12
  • 114
  • 149