防火墙后的WCF WebService / IIS托管和配置问题

28
我有一个简单的WCF Web服务。它托管在我们生产域中默认网站下的IIS上。(本地地址:10.10.20.100)
默认情况下,此默认网站已设置为在端口80上针对“所有未分配”的IP。然而,我注意到这导致WCF服务使用服务器本地DNS名称生成其WSDL。即,wsdl中的所有URI都是:
http://myserver.subdomain.domain.com/.../...

由于我需要将此服务暴露给没有生产环境内部DNS知识的站点,因此这种方法不可行。而且该特定服务器没有外部DNS名称,只有外部IP地址...

我在IIS中更改了设置,从“全部未分配” - >“10.10.20.100”,并取得了一些成功。

这导致服务使用这些URI生成它的WSDL

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

这对于同一子域内和其他子域内的其他机器来说都是可以的,但是我在这里卡住了。服务器的外部IP地址(1.2.3.4)通过某些NAT / PAT翻译进行映射,因此它没有在服务器的IP设置中明确设置(即不显示在IP配置下)。

所以,如果我像为内部地址那样将IIS默认网站IP地址从“所有未分配”更改为“1.2.3.4”,那么WCF服务就会返回...

Bad Request (Invalid Hostname)

而如果我将IIS配置为使用内部IP地址,并尝试通过外部IP地址访问服务,则会出现以下情况:

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

有没有办法让IIS / WCF生成其WSDL URI,其中包含未在服务器上明确配置的外部IP地址?

请有人帮帮我,在我把WCF服务踢出窗外之前。


2
这是一个好问题。为什么像这样的问题没有得到赞,但是像“厌倦了编写GUI /业务应用程序,下一步该怎么做?”这样的问题在几分钟内就得到了4个赞? - Rob
很遗憾,我没有答案可以给你,但我可以给你一个赞。+1。 - Rob
感谢大家迄今为止的回答。我今天将尝试Steve的建议,通过覆盖ServiceHost来解决问题,如果这不起作用,我将咬紧牙关让系统管理员为我们的API服务器获取一个新的FQDN。 - Eoin Campbell
这是解决此问题的解决方案:https://dev59.com/bmHVa4cB1Zd3GeqPm3AR#9671430 - user2047202
6个回答

8

这是因为您没有设置主机标头。这似乎是一个非常普遍的问题,我经常遇到。它生成的uri没有配置,它通过检查站点的主机标头来查找正确的地址。即使它在虚拟目录中,您也需要转到父目录(在您的情况下是默认目录),并添加主机标头。

如果您不知道如何操作,请告诉我。


我曾经遇到过同样的问题,但是通过这个解决方案解决了。不过,我认为它只影响https托管。也许不是这样。我使用adsutil.vbs脚本在IIS服务器上添加了主机头。它强制443端口具有特定的主机名,而不是默认值。 - Andy McCluggage
但我知道您可以为不安全的绑定设置主机标头,所以也许值得一试。 - Andy McCluggage
如何添加主机头? - Omu
1
http://technet.microsoft.com/en-us/library/cc753195%28v=ws.10%29.aspx 提供了关于设置主机头的指导方向。 - DougWebb

1

以下是如何在IIS7中更改标题的方法。如果需要,也可以放置我的web.config文件。

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>

1

如果我能理解你的问题,我可能可以防止忍者暴力......

您可以手动指定服务在web.config中应使用的完整地址,而不是让ServiceHost为您找出它。您必须设置服务的基本地址:

 <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>

使用此方法,您的服务应该接受指定的基地址和服务名称,以及如果有的话,附加的终结点地址。此外,您需要使用自定义的ServiceHostFactory来以编程方式设置基地址。请参见下面的示例:
    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;
    }

最后,一旦您建立了ServiceHostFactory类,您必须通过编辑.svc文件中的标记来将其连接到您的服务:
<%@ ServiceHost Language="C#" Debug="true" Service="Api.Poll" Factory="Api.ServiceHostFactory" CodeBehind="Poll.svc.cs" %>

当在IIS下托管时,web.config的BaseAddress部分完全被忽略。无论是否存在,都没有任何影响。IIS根据虚拟目录配置确定地址。 https://dev59.com/-XVD5IYBdhLWcg3wL4mM - Eoin Campbell
1
嗯...我之前在IIS下使用过它,但那是一个有多个基础地址的不同情况。我想知道是否可以使用ServiceHostFactory来以编程方式设置基础地址。请参见修订后的答案。 - Steve
感谢你的回复,Steve。这里是格林威治标准时间晚上9点,所以我要等回到办公室后再试一下,明天再回来和你联系。你能否澄清一下我应该在哪里创建这个类?我是把它放到App_Code文件夹中,然后让我的服务继承它吗? - Eoin Campbell
是的,你可以把它放在App_Code部分,那样就可以了。不过还有最后一步要连接起来,你需要修改.svc文件的标记。我已经更新了答案以包括这一点。 - Steve

1

必须使用IP地址而不是FQDN吗?通过将其更改为FQDN并在站点的主机标头中设置,然后通过绑定到它来实现。

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

然后回收应用程序池将在生成的WSDL中产生该主机名。这样做有好处-您可以设置一个内部DNS,将该FQDN解析为内部IP,以及一个解析到防火墙IP的外部DNS,然后同一系统将无需任何更改即可工作。


1

尽管这是一个旧的线程,但它实际上帮助我将一个项目从VS Web Developer Express迁移到了MonoDevelop,其中包括一个WCF服务。

Web应用程序使用以下URL请求由WCF服务定义的JavaScript接口:http://127.0.0.1:8080/path-to-service/service.svc/js,这给了我错误:没有协议绑定与给定地址匹配

受到这个线程的启发,我能够解决问题,因为使用localhost而不是127.0.0.1访问服务可以解决问题!通过对http://localhost:8080/path-to-service/service.svc/js进行请求,我得到了JavaScript接口。

实际上,我的应用程序没有使用绝对URL来包含JavaScript接口,但是默认情况下,当从MonoDevelop启动应用程序时,它会使用127.0.0.1访问应用程序,因此调用来自服务的JavaScript失败。

这还不完美,因为我无法使用localhost而不是127.0.0.1从MonoDevelop启动应用程序,因为XSP的配置只允许我指定一个IP地址,但至少我知道如何解决它。


1
问题在于,如果您更改了端口或IP地址,则应重新启动您正在使用的应用程序池,因为它仍然保留旧信息,直到被回收。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接