Asp.net自托管WCF服务WSDL相对路径问题

3
我正在开发一款WCF应用程序,将在多个服务器上部署,我希望不必每次部署时都记得更改app.config文件。起初,我的app.config配置文件中的serviceModel部分如下所示:
<system.serviceModel>  
<serviceHostingEnvironment aspNetCompatibilityEnabled="false" />  
<behaviors>  
    <serviceBehaviors>  
        <behavior name="MyDefaultServiceBehavior">   
            <serviceMetadata httpGetEnabled="true" httpGetUrl="http://localhost:8888/MyService" />  
            <serviceDebug includeExceptionDetailInFaults="true" />  
        </behavior>  
    </serviceBehaviors>  
</behaviors>  
<services>  
    <service behaviorConfiguration="MyDefaultServiceBehavior" name="MyService">   
        <endpoint address="net.tcp://localhost:9001/MyService" binding="netTcpBinding" contract="IMyService" name="NetTcpBinding_IMyService" />  
    </service>  
</services>  

当我访问在本地机器上运行的服务时,这个很好用。但是当我部署它时,WSDL包含了仍然指向本地主机的绝对路径:

<xsd:import schemaLocation=http://localhost:8888/MyService?xsd=xsd0 namespace="http://tempuri.org/" />

所以,我可以在app.config中像这样更改httpGetUrl:
<serviceMetadata httpGetEnabled="true" httpGetUrl=http://devserver1:8888/MyService />

现在 WSDL 在该服务器上可以正常工作了。问题是我必须在每个被部署的 app.config 文件中手动设置地址。

有没有办法:

  1. 让 WSDL 包含所有内容,以便不需要导入任何东西?
  2. 在 WSDL 导入语句中使用相对路径?

如果有其他建议,将不胜感激。我有两个开发服务器需要自动化部署,但就是因为这个 WSDL 问题才不能实现。

既然这只是为了生成代理,我想我可以自己生成代理并分发,但我更想让用户自己生成代理。

谢谢! Daniel

3个回答

3
您可以通过编程设置httpGetUrl的值,并将其设置为包含托管服务的服务器的机器名称的绝对地址。生成的WSDL中的导入语句也将使用服务器的机器名称。
如果您的WCF主机是由系统自动创建的(例如,您在IIS下进行托管),则需要创建一个自定义的ServiceHostFactory来访问ServiceHost。例如:
using System;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Description;

namespace WebApplication
{
  public class TestServiceHostFactory : ServiceHostFactory
  {
     protected override ServiceHost CreateServiceHost(Type serviceType, 
                                                      Uri[] baseAddresses)
      {
        ServiceHost host = base.CreateServiceHost(serviceType, 
                                                  baseAddresses);
        ServiceMetadataBehavior metadataBehavior = 
                                new ServiceMetadataBehavior();
        metadataBehavior.HttpGetEnabled = true;
        metadataBehavior.HttpGetUrl = new Uri(string.Format(
                              "http://{0}/WebApplication/TestService.svc", 
                              Environment.MachineName));
        host.Description.Behaviors.Add(metadataBehavior);
        return host;
      }
  }
}

然后您需要在服务的 .svc 文件中指定此工厂:

<%@ ServiceHost Language="C#" 
                Service="WebApplication.TestService" 
                CodeBehind="TestService.svc.cs" 
                Factory="WebApplication.TestServiceHostFactory" %>

如果您自己创建WCF主机,则代码将类似于以下内容:
ServiceHost host = new ServiceHost(typeof(WebApplication.TestService));
ServiceMetadataBehavior metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetEnabled = true;
metadataBehavior.HttpGetUrl = new Uri(string.Format(
                              "http://{0}/WebApplication/TestService.svc", 
                              Environment.MachineName));
host.Description.Behaviors.Add(metadataBehavior);

这基本上就是我做事的方式。 - Sailing Judo
1
对我而言,更大的问题是并非每个人都可以使用机器名称访问服务器。对于一些人,它甚至无法解析。您的解决方案是我的问题的良好答案,但不幸的是,我将问题简化了太多,它不能解决我的问题。请参见其他评论以获取解决方案。 - Daniel

2

上面由Daniel Richardson提供的答案很好,我认为对于大多数人来说,那是首选解决方案。但是,由于我们的网络布局以及只有少数人需要访问我们的服务器,我正在做一些略微不同的事情。

我已经更改了我的app.config,其中包含一个httpGetUrl,其中包含“myServiceServer”:

<serviceMetadata httpGetEnabled="true" httpGetUrl=http://myServiceServer:8888/MyService />

要使用我的服务,首先必须在主机文件中添加一个条目将“myServiceServer”映射到正确的IP地址。这对我们的问题非常有效,因为从任何普通的机器名称或IP地址都无法解析出IP地址。这是由于仅通过带有某种NAT的VPN连接的分离网络导致的。

0

显然,WCF中有一个非常隐秘的选项支持使用与传入请求相同的主机名,这通常是正确的。(我认为它不是默认值的唯一原因是为了向后兼容性,尽管如果他们把它设置为默认值可能会更好。)

在我找到这个特定的宝石之前,我进行了很多搜索和挠头(虽然现在我知道了魔术词汇,我找到了一个来自类似旅程的另一个答案)。

要启用它,请在添加ServiceMetadataBehavior的相同位置添加以下代码:

host.Description.Behaviors.Remove<UseRequestHeadersForMetadataAddressBehavior>();
host.Description.Behaviors.Add(new UseRequestHeadersForMetadataAddressBehavior());

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