17

我刚刚开始使用 ServiceStack,作为一个测试用例,我正在寻找使用标准 ASP.Net 处理程序构建的现有服务。我已经设法让它按我想要的方式工作,但在某些方面使用了 ASP.Net Session 对象。

我尝试将 IRequiresSessionState 添加到服务接口中:

public class SessionTestService : RestServiceBase<SessionTest>, IRequiresSessionState {
    public override object OnPost(SessionTest request) {
        // Here I want to use System.Web.HttpContext.Current.Session
    }
}

问题是我似乎无法让它工作,因为 Session 对象始终为空。

我已经做了很多谷歌搜索并对https://github.com/mythz/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Services/Secure.cs和类似的东西感到困惑,但我找不到任何执行此操作的示例代码(这让我感到惊讶)。谁能解释为什么上述方法不起作用并建议我需要做什么才能使其正常工作?

注意:最终我可能会考虑用 Redis 替换它,或者会尝试删除任何服务器端会话要求,但我认为我暂时使用 ASP.Net 实现,以使事情正常工作并避免对其进行返工在这一点上是必要的。

4

4 回答 4

29

使用 ServiceStack ISession

ServiceStack 有一个ISession支持的新接口ICacheClient,允许您ISession在 MVC 控制器、ASP.NET 基本页面和 ServiceStack 的 Web 服务之间共享相同的内容,它们共享相同的 Cookie Id,允许您在这些 Web 框架之间自由共享数据。

注意:ISession 是一个干净的实现,它使用 ServiceStack 自己的组件完全绕过现有的 ASP.NET 会话,如ServiceStack 的 MVC PowerPack中所述,并在Sessions wiki 页面中进行了详细说明。

为了轻松使用 ServiceStack 的 Session(缓存和 JSON 序列化器),让您的控制器继承自ServiceStackController(在 MVC 中)或PageBase(在 ASP.NET 中)

ServiceStack 中还添加了新的身份验证/验证功能,您可以在 wiki 上阅读:

使用 ASP.NET 会话

本质上, ServiceStack只是一组运行在 ASP.NET 或 HttpListener 主机上的轻量级IHttpHandler 。如果托管在 IIS/ASP.NET(最常见)中,它就像普通的 ASP.NET 请求一样工作。

ServiceStack 中的任何内容都不会访问或影响底层 ASP.NET 应用程序中配置的缓存和会话提供程序。如果要启用它,则需要在 ASP.NET 中(即在 ServiceStack 之外)按正常方式对其进行配置,请参阅:

http://msdn.microsoft.com/en-us/library/ms178581.aspx

配置完成后,您可以通过单例访问 ServiceStack Web 服务中的 ASP.NET 会话:

HttpContext.Current.Session

或者通过底层 ASP.NET HttpRequest 使用:

var req = (HttpRequest)base.RequestContext.Get<IHttpRequest>().OriginalRequest;
var session = req.RequestContext.HttpContext.Session;

虽然由于默认情况下强制依赖 XML 配置和性能下降,但我更喜欢避免使用 ASP.NET 的 Session,而是选择使用ServiceStack 中包含的更干净的缓存客户端。

基本上,会话的工作方式(包括 ASP.NET)是将包含唯一 id的 cookie添加到唯一标识浏览器会话的响应中。这个id指向服务器上一个匹配的字典/集合,它代表浏览器的会话。

默认情况下,您链接到的IRequiresSession接口不执行任何操作,它只是一种向自定义请求过滤器或基础 Web 服务发出此请求需要进行身份验证的信号(即您应该放置验证/身份验证逻辑的两个地方)在 ServiceStack 中)。

这是一个基本身份验证实现,它查看 Web 服务是否安全,如果是,请确保它们已通过身份验证。

这是另一个身份验证实现,它验证所有标有[Authenticate]属性的服务,以及如何通过在请求 DTO 上添加属性来为您的服务启用身份验证。

ServiceStack 中的新身份验证模型

上述实现是 ServiceStack 下一版本中包含的多身份验证提供程序模型的一部分。下面的参考示例展示了如何在您的应用程序中注册和配置新的 Auth 模型。

身份验证策略

新的 Auth 模型完全是一种选择加入的便利,因为您可以简单地不使用它并使用请求过滤器或在基类中自己实现类似的行为(通过覆盖OnBeforeExecute)。事实上,新的 Auth 服务实际上并没有内置到 ServiceStack 本身中。整个实现存在于可选的ServiceStack.ServiceInterfaces 项目中,并使用自定义请求过滤器实现。

以下是我多年来使用的不同身份验证策略:

  • 使用[Attribute]标记需要身份验证的服务。可能是最惯用的 C# 方式,当会话 ID通过 Cookie 传递时非常理想。

  • 尤其是在 Web 上下文之外,有时使用更明确的IRequiresAuthentication接口会更好,因为它提供了对身份验证所需的用户和 SessionId 的强类型访问。

  • 您可以只使用 1-liner 对需要它的每个服务进行身份验证 - 在临时的基础上。当您需要身份验证的服务很少时,这是一种合适的方法。

于 2011-12-05T02:10:12.110 回答
19

这是@mythz 的一个伟大而全面的答案。但是,当尝试通过HttpContext.Current.SessionServiceStack Web 服务访问 ASP.NET 会话时,它总是null为我返回。这是因为 ServiceStack 中的任何 HttpHandler 都没有使用IRequiresSessionState接口进行修饰,因此 .NET Framework 没有为我们提供会话对象。

为了解决这个问题,我实现了两个新类,它们都使用装饰器模式来提供我们需要的东西。

首先,一个IHttpHandler需要会话状态的新的。它包装了IHttpHandlerServiceStack 提供的内容并将调用传递给它......

public class SessionHandlerDecorator : IHttpHandler, IRequiresSessionState {
    private IHttpHandler Handler { get; set; }

    internal SessionHandlerDecorator(IHttpHandler handler) {
        this.Handler = handler;
    }

    public bool IsReusable {
        get { return Handler.IsReusable; }
    }

    public void ProcessRequest(HttpContext context) {
        Handler.ProcessRequest(context);
    }
}

接下来,在将返回的处理程序包装到我们的IHttpHandlerFactory新...IHttpHandlerSessionHandlerDecorator

public class SessionHttpHandlerFactory : IHttpHandlerFactory {
    private readonly static ServiceStackHttpHandlerFactory factory = new ServiceStackHttpHandlerFactory();

    public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated) {
        var handler = factory.GetHandler(context, requestType, url, pathTranslated);
        return handler == null ? null : new SessionHandlerDecorator(handler);
    }

    public void ReleaseHandler(IHttpHandler handler) {
        factory.ReleaseHandler(handler);
    }

}

然后,只需将typeWeb.config 中的处理程序中的属性更改为SessionHttpHandlerFactory而不是ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack,您的 Web 服务现在应该可以使用 ASP.NET 会话了。

尽管如此,我完全赞同ISessionServiceStack 提供的新实现。然而,在某些情况下,在一个成熟的产品上,用新的实现替换所有使用 ASP.NET 会话的工作似乎太大了,因此这个解决方法!

于 2012-02-08T13:00:03.687 回答
4

感谢@Richard 您在上面的回答。我正在运行一个新版本的服务堆栈,他们已经使用 HttpFactory 删除了 ServiceStackHttpFactory。而不是拥有

private readonly static ServiceStackHttpHandlerFactory factory = new ServiceStackHttpHandlerFactory();

你需要有

private static readonly HttpHandlerFactory Factory = new HttpHandlerFactory();

这是此服务的更新代码

using ServiceStack;
using System.Web;
using System.Web.SessionState;

namespace MaryKay.eCommerce.Mappers.AMR.Host
{
public class SessionHttpHandlerFactory : IHttpHandlerFactory
{
    private static readonly HttpHandlerFactory Factory = new HttpHandlerFactory();
    public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
    {
        var handler = Factory.GetHandler(context, requestType, url, pathTranslated);
        return handler == null ? null : new SessionHandlerDecorator(handler);
    }

    public void ReleaseHandler(IHttpHandler handler)
    {
        Factory.ReleaseHandler(handler);
    }
}

public class SessionHandlerDecorator : IHttpHandler, IRequiresSessionState
{
    private IHttpHandler Handler { get; set; }

    internal SessionHandlerDecorator(IHttpHandler handler)
    {
        Handler = handler;
    }

    public bool IsReusable
    {
        get { return Handler.IsReusable; }
    }

    public void ProcessRequest(HttpContext context)
    {
        Handler.ProcessRequest(context);
    }
}
}
于 2015-12-15T17:28:35.537 回答
2

从 ServiceStack 4.5+ 开始,HttpHandler 也可以支持异步。像这样:

namespace FboOne.Services.Host
{
  public class SessionHttpHandlerFactory : IHttpHandlerFactory
  {
    private static readonly HttpHandlerFactory Factory = new HttpHandlerFactory();

    public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
    {
      var handler = Factory.GetHandler(context, requestType, url, pathTranslated);
      return handler == null ? null : new SessionHandlerDecorator((IHttpAsyncHandler)handler);
    }

    public void ReleaseHandler(IHttpHandler handler)
    {
      Factory.ReleaseHandler(handler);
    }
  }

  public class SessionHandlerDecorator : IHttpAsyncHandler, IRequiresSessionState
  {
    private IHttpAsyncHandler Handler { get; set; }

    internal SessionHandlerDecorator(IHttpAsyncHandler handler)
    {
      Handler = handler;
    }

    public bool IsReusable
    {
      get { return Handler.IsReusable; }
    }

    public void ProcessRequest(HttpContext context)
    {
      Handler.ProcessRequest(context);
    }

    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
    {
      return Handler.BeginProcessRequest(context, cb, extraData);
    }

    public void EndProcessRequest(IAsyncResult result)
    {
      Handler.EndProcessRequest(result);
    }
  }
}
于 2016-11-13T12:25:14.530 回答