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
如果你想及时得到个人撰写文章以及著作的消息推送,或者想看看个人推荐的技术资料,可以扫描左边二维码(或者长按识别二维码)关注个人公众号(原来公众帐号 蒋金楠的自媒体将会停用)。
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
目录
打赏
0
0
0
0
16429
分享
相关文章
|
7月前
|
快速上手基于 BaGet 的脚本自动化构建 .net 应用打包
本文介绍了如何使用脚本自动化构建 `.net` 应用的 `nuget` 包并推送到指定服务仓库。首先概述了 `BaGet`——一个开源、轻量级且高性能的 `NuGet` 服务器,支持多种存储后端及配置选项。接着详细描述了 `BaGet` 的安装、配置及使用方法,并提供了 `PowerShell` 和 `Bash` 脚本实例,用于自动化推送 `.nupkg` 文件。最后总结了 `BaGet` 的优势及其在实际部署中的便捷性。
282 10
2025年全面的.NET跨平台应用框架推荐
2025年全面的.NET跨平台应用框架推荐
140 23
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
JWT Bearer 认证在 .NET Core 中的应用
【10月更文挑战第30天】JWT(JSON Web Token)是一种开放标准,用于在各方之间安全传输信息。它由头部、载荷和签名三部分组成,用于在用户和服务器之间传递声明。JWT Bearer 认证是一种基于令牌的认证方式,客户端在请求头中包含 JWT 令牌,服务器验证令牌的有效性后授权用户访问资源。在 .NET Core 中,通过安装 `Microsoft.AspNetCore.Authentication.JwtBearer` 包并配置认证服务,可以实现 JWT Bearer 认证。具体步骤包括安装 NuGet 包、配置认证服务、启用认证中间件、生成 JWT 令牌以及在控制器中使用认证信息
223 2
.NET 3.5 中 HttpWebRequest 的核心用法及应用
【9月更文挑战第7天】在.NET 3.5环境下,HttpWebRequest 类是处理HTTP请求的一个核心组件,它封装了HTTP协议的细节,使得开发者可以方便地发送HTTP请求并接收响应。本文将详细介绍HttpWebRequest的核心用法及其实战应用。
280 6
跨域联姻:React.NET——.NET应用与React的完美融合,解锁前后端高效协作新姿势。
【8月更文挑战第28天】探索React.NET,这是将热门前端框架React与强大的.NET后端无缝集成的创新方案。React以其组件化和虚拟DOM技术著称,能构建高性能、可维护的用户界面;.NET则擅长企业级应用开发。React.NET作为桥梁,使.NET应用轻松采用React构建前端,并优化开发流程与性能。通过直接托管React组件,.NET应用简化了部署流程,同时支持服务器端渲染(SSR),提升首屏加载速度与SEO优化。
217 1
WPF开发者必读:揭秘ADO.NET与Entity Framework数据库交互秘籍,轻松实现企业级应用!
【8月更文挑战第31天】在现代软件开发中,WPF 与数据库的交互对于构建企业级应用至关重要。本文介绍了如何利用 ADO.NET 和 Entity Framework 在 WPF 应用中访问和操作数据库。ADO.NET 是 .NET Framework 中用于访问各类数据库(如 SQL Server、MySQL 等)的类库;Entity Framework 则是一种 ORM 框架,支持面向对象的数据操作。文章通过示例展示了如何在 WPF 应用中集成这两种技术,提高开发效率。
145 0
从怀旧到革新:看WinForms如何在保持向后兼容性的前提下,借助.NET新平台的力量实现自我进化与应用现代化,让经典桌面应用焕发第二春——我们的WinForms应用转型之路深度剖析
【8月更文挑战第31天】在Windows桌面应用开发中,Windows Forms(WinForms)依然是许多开发者的首选。尽管.NET Framework已演进至.NET 5 及更高版本,WinForms 仍作为核心组件保留,支持现有代码库的同时引入新特性。开发者可将项目迁移至.NET Core,享受性能提升和跨平台能力。迁移时需注意API变更,确保应用平稳过渡。通过自定义样式或第三方控件库,还可增强视觉效果。结合.NET新功能,WinForms 应用不仅能延续既有投资,还能焕发新生。 示例代码展示了如何在.NET Core中创建包含按钮和标签的基本窗口,实现简单的用户交互。
129 0
Spring 框架里竟藏着神秘魔法?国际化与本地化的奇妙之旅等你来揭开谜底!
【8月更文挑战第31天】在软件开发中,国际化(I18N)与本地化(L10N)对于满足不同地区用户需求至关重要。Spring框架提供了强大支持,利用资源文件和`MessageSource`实现多语言文本管理。通过配置日期格式和货币符号,进一步完善本地化功能。合理应用这些特性,可显著提升应用的多地区适应性和用户体验。
75 0
Xamarin 与 .NET:解锁现代化移动应用开发的超级武器——深入探讨C#与.NET框架如何赋能跨平台应用,实现高效编码与卓越性能
【8月更文挑战第31天】Xamarin 与 .NET 的结合为开发者提供了强大的平台,用于构建现代化移动应用。通过 C# 和 .NET 框架,Xamarin 可以实现一次编写、多平台运行,覆盖 iOS、Android 和 Windows。这种方式不仅节省了开发时间和成本,还保证了应用的一致性和高质量。Xamarin 是一个开源框架,专为跨平台移动应用开发设计,允许使用 C# 语言和 .NET 核心库构建原生应用,并访问各平台特定功能。微软维护的 Xamarin 是 Visual Studio 生态系统的一部分,极大地提高了开发效率。
133 0

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等