11

我需要创建一个托管在 IIS 中的 WCF 服务,使用 http 传输并在服务器内存中保持状态。虽然我知道有状态服务不是一个好主意,但最后一个约束对于使服务与旧客户端一起工作是必要的。

我的第一个想法是在 asp.net 的 session 中存储这些值。我在我的服务中激活了 asp.net 兼容模式,这使我可以访问 HttpContext,但放置在会话对象中的值没有保留在内存中。我认为这是因为处理会话状态的 http 模块未正确配置,但是当我在谷歌上搜索答案时,我遇到了 WCF 会话,并认为使用它们可能是一个更好的主意。

但是,WCF 会话似乎在记录下并在服务上放置了一组奇怪的先决条件,而且我无法找到适合我需要的配置:必须托管在 IIS 中,必须使用 http 或 https 传输,并且可以'不回复​​ Windows 身份验证,因为客户端和服务器不会属于同一域。我正在尝试使用 wsHttpBinding 来实现这一点,我听说 WCF 会话需要安全或可靠消息,但是: - 使用标准绑定并且当服务器不属于同一域时,它会失败并显示“SecurityNegotiationException The caller未通过服务验证”异常。这是相当合乎逻辑的,因为它使用的是 Windows 安全性。

  • 如果我禁用安全完成,则会失败并显示“合同需要会话,但绑定 'WSHttpBinding' 不支持它或未正确配置以支持它。”</p>

  • 如果在禁用安全性的同时启用可靠消息,则会收到异常“绑定验证失败,因为 WSHttpBinding 不支持基于传输安全 (HTTPS) 的可靠会话。无法打开通道工厂或服务主机。使用消息安全性通过 HTTP 进行安全可靠的消息传递。”</p>

  • 我已经尝试启用传输级安全性,但这似乎对生成的错误没有任何影响

有没有适合我的配置?还是我应该回到使用 asp.net 会话的计划?

4

2 回答 2

25

您可以让 WCF 以一种非常简单的方式将会话信息保存在内存中。为了消除我的说明中任何可能的外部影响,我假设您从一个全新的项目开始:

  1. 创建一个新的 WCF 服务库项目。该项目将已经包含一个WSHttpBiding预先配置了绑定的服务。
  2. 转到服务合同 (IService1.cs) 并将 ServiceContract 属性更改为以下内容:

    [ServiceContract(SessionMode = SessionMode.Required)]
    
  3. 转到服务实现 (Service1.cs) 并将以下 ServiceBehavior 属性添加到服务类 ( Service1):

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
    
  4. 将会话数据添加为服务类 ( Service1) 的成员:

    public class Service1 : IService1
    {
        ...
    
        private string UserFullName { get; set; }
    
        ...
    }
    
  5. 使用成员来呈现特定于会话的数据(记住还要将它们添加到服务合同中,IService1):

    public class Service1 : IService1
    {
        ...
    
        public string Welcome(string fullName)
        {
            UserFullName = fullName ?? "Guest";
            return string.Format("Welcome back, {0}!", UserFullName);
        }
    
        public string Goodbye()
        {
            return string.Format("Come back soon, {0}!", UserFullName ?? "Guest");
        }
    
        ...
    }
    

SessionMode.Required确保您的客户被会话跟踪。
InstanceContextMode.PerSession确保为每个会话创建服务类 (Service1) 的实例,以便您可以在其中保留会话数据,并且它将在同一会话中的多个调用中存在于内存中。
ConcurrencyMode.Single确保只有一个线程可以进入每个服务类实例 (Service1),并防止在您仅从服务类(和外部线程安全位置)访问数据时可能出现的并发问题。

编辑:默认情况下,WSHttpBinding只允许安全会话。但它也支持可靠会话,允许在没有启用安全性的情况下建立会话。以下绑定配置禁用安全性并启用可靠会话:

<wsHttpBinding>
    <binding name="wsHttpBindingConfiguration">
        <security mode="None" />
        <reliableSession enabled="true" />
    </binding>
</wsHttpBinding>
于 2011-01-22T14:40:19.500 回答
1

IMO 当您使用 WCF 等对 HTTP 的抽象较差的技术时,就会发生这种情况。事实上,理论上 WCF Web 服务可以在没有 HTTP 的情况下托管(即通过 NET TCP、MSMQ 等),这使得在不进入配置地狱的情况下很难使用 HTTP 的内置功能并开始“猜测正确配置”的游戏通过反复试验”,您可以尝试所有可能的配置排列,直到找到正确的配置排列!

最终,如果您不能使用 WCF 并且必须从头开始实施 Web 服务,您只需在客户端成功通过身份验证时设置一个 cookie。然后对于每个客户端请求,只需获取该 cookie 引用的会话信息。

如果您必须使用 WCF,一种可能的解决方案是自己进行会话管理(当我对让某些东西工作所需的努力不满意时,我会这样做)并在您的所有网络上都有一个明确的“会话”属性需要会话/身份验证的服务(通常是在身份验证时生成的 guid)。因此,对于每个后续请求,您都使用 guid 来补充与该客户端关联的会话信息。

如果您有兴趣尝试不同的 Web 服务框架,我会维护一个开源 Web 服务框架,它可以让您构建无配置、DRY、可测试的 Web 服务,其中(无需任何配置)您创建的每个 Web 服务都可以通过 REST 自动访问XML、JSON、JSV、SOAP 1.1、SOAP 1.2 端点。实际上,它允许您通过 REST-ful 客户端的 HTTP GET url 访问相同的 Web 服务,并且易于调试以及 SOAP 端点(一些企业仍然强制要求的流行选择)。Hello World教程应该可以很好地概述它的一些功能以及它是如何工作的。

于 2011-01-22T12:02:15.570 回答