如何在ServiceStack服务实现中使用标准ASP.NET会话对象

时间:2011-12-01 10:11:25

标签: asp.net servicestack

我刚刚开始使用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对象始终为null。

我已经做了很多谷歌搜索并且对https://github.com/mythz/ServiceStack/blob/master/tests/ServiceStack.WebHost.IntegrationTests/Services/Secure.cs和类似的事情感到困惑但我找不到任何这样做的代码(让我感到惊讶)。任何人都可以解释为什么以上不起作用并建议我需要做些什么才能使其正常工作?

注意:最终我可能希望用Redis替换它,或者尝试删除任何服务器端会话要求,但我认为我暂时使用ASP.Net实现,为了使事情有效,并避免在此时更多地重新加工。

4 个答案:

答案 0 :(得分:28)

使用ServiceStack ISession

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

注意:ISession是一个干净实现,它完全绕过现有的ASP.NET会话和Service ServiceStack's MVC PowerPack中描述的ServiceStack自己的组件,并在{{3}中详细解释}。

要轻松使用ServiceStack的会话(缓存和JSON序列化程序),您的控制器将继承自Sessions wiki page(在MVC中)或ServiceStackController(在ASP.NET中)

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

使用ASP.NET会话

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

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

ServiceStack

配置完成后,您可以通过单例访问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配置和http://msdn.microsoft.com/en-us/library/ms178581.aspx,我宁愿避免使用ASP.NET的Session,而是选择使用ServiceStack附带的清除degraded performance by default

基本上,Sessions的工作方式(包括ASP.NET)是一个包含唯一ID 的cookie,它被添加到唯一标识浏览器会话的Response中。此 id 指向服务器上匹配的Dictionary / Collection,代表浏览器的Session。

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

这是一个Cache Clients,用于查看网络服务是否安全,如果是,请确保他们已通过身份验证。

以下Basic Auth implementation代替验证标有 [Authenticate] 属性的所有服务,并通过在您的请求DTO上添加属性来验证another authentication implementation

ServiceStack中的新身份验证模型

上述实现是ServiceStack下一版本中包含的多身份验证提供程序模型的一部分。这里是how to enable Authentication for your service,展示了如何在应用程序中注册和配置新的Auth模型。

身份验证策略

新的Auth模型完全是一种选择上的便利,因为您可以使用请求过滤器基类(通过覆盖)自行使用它并实现类似的行为the reference example)。事实上,新的Auth服务实际上并没有内置到ServiceStack本身。整个实现位于可选的OnBeforeExecute中,并使用自定义请求过滤器实现。

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

  • 使用 [Attribute] 标记需要身份验证的服务。可能是最惯用的C#方式,非常适合通过Cookie传递 session-id

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

    < / LI>
  • 您可以在每个需要它的服务上使用1-liner进行身份验证 - 在特定情况下。当您很少需要身份验证的服务时,这是一种合适的方法。

答案 1 :(得分:18)

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

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

首先,需要会话状态的新IHttpHandler。它包装ServiceStack提供的IHttpHandler并将调用传递给它......

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将生成IHttpHandler的责任委托给ServiceStack,然后将返回的处理程序包装到新的SessionHandlerDecorator中......

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);
    }

}

然后,只需将Web.config中处理程序中的type属性更改为SessionHttpHandlerFactory而不是ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack,您的Web服务现在应该具有ASP.NET会议对他们有利。

尽管如此,我完全赞同ServiceStack提供的新ISession实现。但是,在某些情况下,在成熟的产品上,用新的实现替换ASP.NET会话的所有用途似乎太大了,因此这种解决方法!

答案 2 :(得分: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);
    }
}
}

答案 3 :(得分:2)

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

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);
    }
  }
}