Как я могу использовать стандартный объект сеанса ASP.NET в реализации службы ServiceStack - PullRequest
16 голосов
/ 01 декабря 2011

Я только начинаю работать с ServiceStack и, в качестве тестового примера, я пытаюсь переделать существующий сервис, который построен с использованием стандартных обработчиков ASP.Net. Мне удалось заставить все работать так, как я хочу, но у меня есть некоторые аспекты, которые используют объект сеанса ASP.Net.

Я пытался добавить 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 ]

28 голосов
/ 05 декабря 2011

Использование ServiceStack ISession

ServiceStack имеет новый ISession интерфейс, поддерживаемый ICacheClient, который позволяет вам совместно использовать те же ISession между контроллерами MVC, базовыми страницами ASP.NET и веб-службами ServiceStack, которые используют один и тот же идентификатор cookie, позволяя вам свободно обмениваться данными эти веб-фреймворки.

Примечание: ISession - это чистая реализация , которая полностью обходит существующий сеанс ASP.NET с собственными компонентами ServiceStack, как описано в ServiceCtack MVC PowerPack и подробно объяснено в Вики-страница сессий .

Чтобы легко использовать сессию ServiceStack (Cache & JSON Serializer), ваши контроллеры наследуют от ServiceStackController (в MVC) или PageBase (в ASP.NET)

В ServiceStack также добавлены новые функции аутентификации / проверки, о которых вы можете прочитать в вики:

Использование сеанса ASP.NET

По существу ServiceStack - это просто набор облегченных IHttpHandler , работающих на хосте ASP.NET или HttpListener. Если он размещен в IIS / ASP.NET (чаще всего), он работает как обычный запрос ASP.NET.

Ничто в ServiceStack не обращается и не влияет на настроенных поставщиков кэширования и сеансов в базовом приложении ASP.NET. Если вы хотите включить его, вам нужно настроить его как обычно в ASP.NET (то есть за пределами ServiceStack), см .:

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

После настройки вы можете получить доступ к сеансу ASP.NET внутри веб-службы ServiceStack через синглтон:

HttpContext.Current.Session

Или альтернативно через базовый ASP.NET HttpRequest с:

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

Хотя из-за обязательной зависимости от конфигурации XML и ухудшенной производительности по умолчанию , я предпочитаю избегать использования сеанса ASP.NET, вместо этого выбрав очиститель Клиенты кэша включен в ServiceStack.

В основном способ работы Sessions (включая ASP.NET) - это файл cookie, содержащий уникальный идентификатор , добавляемый в Response, однозначно идентифицирующий сеанс браузера. Этот id указывает на соответствующий словарь / коллекцию на сервере, который представляет сеанс браузера.

Интерфейс IRequiresSession , на который вы ссылаетесь, по умолчанию ничего не делает, это просто способ сообщить либо пользовательскому фильтру запросов, либо базовой веб-службе, что этот запрос должен быть аутентифицирован (т.е. два места, где вы должны поместить логику проверки / аутентификации в ServiceStack).

Вот реализация Basic Auth , которая проверяет, является ли веб-служба безопасной и, если это так, убедитесь, что они аутентифицированы.

Вот другая реализация аутентификации , которая вместо этого проверяет все службы, отмеченные атрибутом [Аутентификация] , и как включить Аутентификацию для вашей службы , добавив Атрибут по вашему запросу DTO.

Новая модель аутентификации в ServiceStack

Вышеприведенная реализация отличается от модели поставщика с множественной аутентификацией, включенной в следующую версию ServiceStack. Вот справочный пример , показывающий, как зарегистрировать и настроить новую модель Auth в вашем приложении.

Стратегии аутентификации

Новая модель Auth полностью удобна, так как вы можете просто не использовать ее и реализовать аналогичное поведение самостоятельно, используя Фильтры запросов или базовые классы (путем переопределения OnBeforeExecute ). Фактически, новые сервисы Auth фактически не являются встроенными в ServiceStack как таковые. Вся реализация находится в дополнительном проекте ServiceStack.ServiceInterfaces и реализована с использованием пользовательских фильтров запросов.

Вот различные стратегии аутентификации, которые я использовал на протяжении многих лет:

  • Отметьте службы, которым требуется аутентификация, с помощью [Атрибут] . Вероятно, самый идиоматический способ C #, идеальный, когда идентификатор сессии передается через Cookie.

  • Особенно вне веб-контекста, иногда с использованием более явного интерфейса IRequiresAuthentication лучше, поскольку он обеспечивает строго типизированный доступ к User и SessionId, необходимому для аутентификации.

  • Вы можете просто иметь 1-полосную подпись для проверки подлинности на каждом сервисе, который в этом нуждается, - на временной основе. Подходящий подход, когда у вас очень мало служб, требующих аутентификации.

18 голосов
/ 08 февраля 2012

Это отличный и исчерпывающий ответ @mythz.Однако при попытке доступа к сеансу ASP.NET с помощью HttpContext.Current.Session в веб-службе ServiceStack он всегда возвращает null для меня.Это происходит потому, что ни один из HttpHandlers в ServiceStack не украшен интерфейсом IRequiresSessionState, поэтому .NET Framework не предоставляет нам объект сеанса.

Чтобы обойти это, я реализовал два новых класса:оба из которых используют шаблон декоратора, чтобы предоставить нам то, что нам нужно.

Во-первых, новый IHttpHandler, который требует состояния сеанса.Он оборачивает IHttpHandler, предоставленный ServiceStack, и передает ему вызовы ...

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

}

Затем нужно просто изменить атрибуты type в обработчиках в Web.config на SessionHttpHandlerFactory вместо ServiceStack.WebHost.Endpoints.ServiceStackHttpHandlerFactory, ServiceStack,и ваши веб-службы теперь должны иметь доступ к сеансу ASP.NET.

Несмотря на вышесказанное, я полностью поддерживаю новую реализацию ISession, предоставляемую ServiceStack.Однако в некоторых случаях для зрелого продукта просто кажется слишком большой задачей заменить все виды использования сеанса ASP.NET новой реализацией, поэтому это обходной путь!

4 голосов
/ 15 декабря 2015

Спасибо @Richard за ваш ответ выше.Я использую новую версию стека служб, и они удалили ServiceStackHttpFactory с помощью HttpFactory.Вместо

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);
    }
}
}
2 голосов
/ 13 ноября 2016

Начиная с 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);
    }
  }
}
...