WCF技术剖析之六:为什么在基于ASP.NET应用寄宿(Hosting)下配置的BaseAddress无效

简介:

本篇文章来源于几天前一个朋友向我咨询的问题。问题是这样的,他说他采用ASP.NET应用程序的方式对定义的WCF服务进行寄宿(Hosting),并使用配置的方式对服务的BaseAddress进行了设置,但是在创建ServiceHost的时候却抛出InvalidOperationException,并提示相应Address Scheme的BaseAddress找不到。我意识到这可能和WCF中用于判断服务寄宿方式的逻辑有关,于是我让这位朋友将相同的服务寄宿代码和配置迁移到GUI程序或者Console应用中,看看是否正常。结果如我所想,一切正常,个人觉得这应该是WCF的一个Bug。今天撰文与大家讨论,看看大家对这个问题有何见解。

一、问题重现

问题很容易重现,假设我们通过ASP.NET应用对服务CalculatorService进行寄宿,为了简单起见,我将服务契约和服务实现定义在一起。CalculatorService的定义如下面的代码片断所示:

   1: using System.ServiceModel;
   2: namespace Artech.AspnetHostingDemo
   3: {
   4:     [ServiceContract(Namespace = "urn:artech.com")]
   5:     public class CalculatorService
   6:     {
   7:         [OperationContract]
   8:         public double Add(double x, double y) { return x + y; }
   9:     }
  10: }

下面是服务寄宿相关的配置,在<host>/<baseAddresses>配置节中为服务添加了一个Scheme为http的BaseAddress:http://127.0.0.1:3721/services,那么终结点的地址就可以定义为基于该BaseAddress的相对地址了:calculatorservice。

   1: <?xml version="1.0"?>
   2: <configuration>
   3:   <system.serviceModel>
   4:     <services>
   5:       <service name="Artech.AspnetHostingDemo.CalculatorService">
   6:         <host>
   7:           <baseAddresses>
   8:             <add baseAddress="http://127.0.0.1:3721/services"/>
   9:           </baseAddresses>
  10:         </host>
  11:         <endpoint address="calculatorservice" binding="wsHttpBinding" contract="Artech.AspnetHostingDemo.CalculatorService"/>
  12:       </service>
  13:     </services>
  14:   </system.serviceModel>
  15:   <system.web>
  16:     <compilation debug="true"/>
  17:   </system.web>
  18: </configuration>

我们把服务寄宿的代码定义在一个Web Page的Load事件中。但程序执行到到创建ServiceHost的时候,抛出如下图所示的InvalidOperationException异常。

image

 

下面是错误信息和异常的StackTrace:

   1: Could not find a base address that matches scheme http for the endpoint with binding WSHttpBinding. Registered base address schemes are [].

 

   1: at System.ServiceModel.ServiceHostBase.MakeAbsoluteUri(Uri relativeOrAbsoluteUri, Binding binding, UriSchemeKeyedCollection baseAddresses)   
   2: at System.ServiceModel.Description.ConfigLoader.LoadServiceDescription(ServiceHostBase host, ServiceDescription description, ServiceElement serviceElement, Action`1 addBaseAddress)   
   3: at System.ServiceModel.ServiceHostBase.LoadConfigurationSectionInternal(ConfigLoader configLoader, ServiceDescription description, ServiceElement serviceSection)   
   4: at System.ServiceModel.ServiceHostBase.LoadConfigurationSectionInternal(ConfigLoader configLoader, ServiceDescription description, String configurationName)   
   5: at System.ServiceModel.ServiceHostBase.ApplyConfiguration()   
   6: at System.ServiceModel.ServiceHostBase.InitializeDescription(UriSchemeKeyedCollection baseAddresses)   
   7: at System.ServiceModel.ServiceHost.InitializeDescription(Type serviceType, UriSchemeKeyedCollection baseAddresses)   
   8: at System.ServiceModel.ServiceHost..ctor(Type serviceType, Uri[] baseAddresses)   
   9: at Artech.AspnetHostingDemo._Default.Page_Load(Object sender, EventArgs e) in e:\WCF Projects\AspnetHostingDemo\AspnetHostingDemo\Default.aspx.cs:line 16   
  10: at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)   
  11: at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)   
  12: at System.Web.UI.Control.OnLoad(EventArgs e)   at System.Web.UI.Control.LoadRecursive()   
  13: at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

二、问题分析

通过上面提供StackTrace,我们可以看到错误发生在WCF试图将BaseAddress和RelativeAddress进行组合生成AbsoluteAddress的时候。从错误消息可以看出,在进行地址的组合时,由于没有找到适合绑定类型(WsHttpBinding)Scheme(http)的BaseAddress,导致了异常的抛出。

要解答这个问题,首先要解释一下WCF的BaseAddress在不同服务寄宿(Service Hosting)方式下的定义方式。对于WCF服务的自我寄宿(Self Hosting)或者采用Windows Service进行服务寄宿,我们可以通过代码或者形如上面的配置为服务指定一系列的BaseAddress(对于一个既定的URI Scheme,只能由唯一的BaseAddress)。但是对于采用IIS或者WAS进行服务寄宿,我们需要为相应的服务定义一个.svc文件,我们通过访问.svc文件的方式来调用相应的服务。对于后者,.svc文件得地址就是WCF服务的BaseAddress,所以WCF会忽略BaseAddress的配置。

那么WCF采用怎样的方式来判断当前服务寄宿的方式是基于IIS呢,还是其他呢?答案是通过System.Web.Hosting.HostingEnvironment的静态属性IsHosted。对于ASP.NET有一定了解的人应该很清楚,在一个ASP.NET应用下,该属性永远返回为True。也就是说,WCF会把基于ASP.NET应用的服务寄宿,看成是基于IIS的服务寄宿,这显然是不对的。

   1: public sealed class HostingEnvironment : MarshalByRefObject
   2: {     //其他成员
   3:     public static bool IsHosted { get; }
   4: }

WCF对BaseAddress配置的加载和添加的逻辑定义在ServiceHostBase的LoadHostConfig方法中,大致的逻辑如下面的代码所示:

   1: public abstract class ServiceHostBase : CommunicationObject, IExtensibleObject<ServiceHostBase>, IDisposable
   2: {
   3:     [SecurityTreatAsSafe, SecurityCritical]
   4:     private void LoadHostConfig(ServiceElement serviceElement, ServiceHostBase host, Action<Uri> addBaseAddress)
   5:     {
   6:         HostElement element = serviceElement.Host; if (element != null)
   7:         {
   8:             if (!ServiceHostingEnvironment.IsHosted)
   9:             {                //BaseAddress配置加载与添加
  10:             }
  11:         }
  12:     }
  13: }

 

   1: public static class ServiceHostingEnvironment
   2: {
   3:     private static bool isHosted; internal static bool IsHosted { get { return isHosted; } }
   4:     internal static void EnsureInitialized()
   5:     {
   6:         if (hostingManager == null)
   7:         {
   8:             lock (ThisLock)
   9:             {
  10:                 if (hostingManager == null)
  11:                 {
  12:                     if (!HostingEnvironmentWrapper.IsHosted)
  13:                     {
  14:                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString("Hosting_ProcessNotExecutingUnderHostedContext", new object[] { "ServiceHostingEnvironment.EnsureServiceAvailable" })));
  15:                     }
  16:                     HostingManager manager = new HostingManager();
  17:                     HookADUnhandledExceptionEvent(); 
  18:                     Thread.MemoryBarrier(); 
  19:                     isSimpleApplicationHost = GetIsSimpleApplicationHost();
  20:                     hostingManager = manager; 
  21:                     isHosted = true;
  22:                 }
  23:             }
  24:         }
  25:     }
  26: }
  27:  
   1: internal static class HostingEnvironmentWrapper
   2: {
   3:     public static bool IsHosted
   4:     {
   5:         get { return HostingEnvironment.IsHosted; }
   6:     }
   7: }

三、解决方式

其实这种情况也没有什么好的解决方案,不外乎就是避免通过配置的方式设置服务的BaseAddress,可以通过代码的方式来设置。如下面的代码所示:

   1: namespace Artech.AspnetHostingDemo
   2: {
   3:     public partial class _Default : System.Web.UI.Page
   4:     {
   5:         private ServiceHost _serviceHost;
   6:         protected void Page_Load(object sender, EventArgs e)
   7:         {
   8:             this._serviceHost = new ServiceHost(typeof(CalculatorService), new Uri("http://127.0.0.1:3721/services")); 
   9:             this._serviceHost.Open();
  10:         }
  11:     }
  12: }

另一种方式就是采用绝对地址的方式定义终结点:

   1: <?xml version="1.0"?>
   2: <configuration>
   3:   <system.serviceModel>
   4:     <services>
   5:       <service name="Artech.AspnetHostingDemo.CalculatorService">
   6:         <endpoint address="http://127.0.0.1:3721/services/calculatorservice" binding="wsHttpBinding" contract="Artech.AspnetHostingDemo.CalculatorService"/>
   7:       </service>
   8:     </services>
   9:   </system.serviceModel>
  10:   <system.web>
  11:     <compilation debug="true"/>
  12:   </system.web>
  13: </configuration>

 


作者:蒋金楠
微信公众账号:大内老A
微博: www.weibo.com/artech
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
相关文章
|
5月前
|
算法 Java 调度
|
5天前
|
开发框架 JSON .NET
.Net4.0 Web.config 配置实践
.Net4.0 Web.config 配置实践
|
10天前
|
C# Windows
一款.NET开源、简洁易用的Windows桌面小说阅读应用
一款.NET开源、简洁易用的Windows桌面小说阅读应用
|
12天前
|
开发框架 物联网 测试技术
【专栏】.NET 开发:打造领先应用的基石
【4月更文挑战第29天】本文探讨了.NET开发框架为何成为构建领先应用的首选。高性能与稳定性是.NET的核心优势,它采用先进的技术和优化策略,如.NET Core的轻量级设计和JIT/AOT编译模式。跨平台兼容性让开发者能用相同代码库在不同操作系统上构建应用。现代化的开发体验,如C#语言的创新特性和Visual Studio的强大工具,提升了开发者生产力。丰富的生态系统和广泛支持,包括庞大的开发者社区和微软的持续投入,为.NET提供了坚实后盾。
|
12天前
|
机器学习/深度学习 人工智能 Cloud Native
【专栏】洞察.NET 技术的前沿应用
【4月更文挑战第29天】本文探讨了.NET技术的前沿应用,包括.NET Core的跨平台崛起、云原生及AI/机器学习领域的整合。.NET Core支持多平台运行,开源社区的参与促进了其快速发展和性能优化。Xamarin与.NET MAUI助力跨平台移动应用和统一界面开发,而云原生应用借助.NET Core与Azure云服务得以轻松构建和部署。此外,ML.NET和TensorFlow.NET为.NET开发者提供了机器学习和深度学习工具,推动智能应用和边缘计算的创新。.NET技术正持续演进,引领软件开发新趋势。
|
12天前
|
人工智能 物联网 开发者
【专栏】探究.NET 技术的创新应用
【4月更文挑战第29天】本文探讨了.NET技术的最新进展和创新应用,包括.NET 5及后续版本的统一平台、性能提升、跨平台支持、云集成优化和开源社区的贡献。在创新应用场景中,重点介绍了微服务架构、物联网、AI、游戏开发和移动应用。未来,.NET将持续优化性能,深化云原生应用,集成新兴技术,扩大社区生态,并促进相关教育和培训。开发者应把握.NET技术的潜力,积极参与其发展,创造更多创新软件产品。
|
12天前
|
安全 Linux API
【专栏】.NET 开发:打造卓越应用的秘诀
【4月更文挑战第29天】本文介绍了.NET技术的起源、核心特性和应用场景,揭示了其打造卓越应用的秘诀。自2002年推出,.NET历经发展,现支持跨平台,包括.NET Core和.NET 5。其核心特性包括:跨平台兼容性、面向对象编程、内置安全性和高效性能。丰富的类库、强大的开发工具、简洁的语言语法以及活跃的社区支持,使.NET成为构建高效、安全应用的理想选择。随着技术进步,.NET将持续赋能开发者创造更多可能性。
|
12天前
|
人工智能 安全 API
【专栏】理解 .NET 技术,打造优质应用
【4月更文挑战第29天】本文探讨了如何利用.NET技术构建高质量应用程序,介绍了.NET从2002年发展至今的历程,强调其跨平台能力、高效开发、丰富的类库和API、开源生态及安全性等优势。随着.NET 6的规划,平台将更加统一和跨平台,适应云计算、AI等新兴技术。.NET凭借其特性,成为开发者和企业创新的有力工具,未来将继续扮演重要角色。
|
12天前
|
机器学习/深度学习 自然语言处理 安全
【专栏】.NET 开发:构建智能应用的关键
【4月更文挑战第29天】本文探讨了.NET开发在构建智能应用中的关键作用,强调了其强大的框架、工具集、高效性能和跨平台支持。通过实例展示了.NET在人工智能、物联网及企业级应用中的应用。同时,指出了.NET开发面临的挑战,如技术更新的学习成本、性能优化、资源管理和安全隐私保护,并提出了应对策略。随着技术进步,.NET将在智能应用领域发挥更大作用,推动创新与便利。
|
26天前
|
开发框架 前端开发 JavaScript
采用C#.Net +JavaScript 开发的云LIS系统源码 二级医院应用案例有演示
技术架构:Asp.NET CORE 3.1 MVC + SQLserver + Redis等 开发语言:C# 6.0、JavaScript 前端框架:JQuery、EasyUI、Bootstrap 后端框架:MVC、SQLSugar等 数 据 库:SQLserver 2012
22 0