Доступ/использование одного и того же объекта во время запроса – asp.net

StackOverflow https://stackoverflow.com/questions/1186134

  •  19-09-2019
  •  | 
  •  

Вопрос

у меня есть HttpModule, который создает объект CommunityPrincipal (реализует интерфейс IPrincipal) при каждом запросе.Я хочу каким-то образом хранить объект для каждого запроса, чтобы я мог получить его, когда он мне понадобится, без необходимости выполнять приведение или создавать его снова.

По сути, я хочу имитировать работу FormsAuthenticationModule.Он присваивает свойству HttpContext.User объект, реализующий интерфейс IPrincipal, при каждом запросе.

Я как-то хочу иметь возможность звонить и т. д.HttpContext.MySpecialUser (или MySpecialContext.MySpecialUser — можно создать статический класс), который вернет мой объект (определенный тип).

Я мог бы использовать метод расширения, но не знаю, как сохранить объект, чтобы к нему можно было получить доступ во время запроса.

Как этого можно достичь?

Обратите внимание, что я хочу сохранить его как определенный тип (CommunityPrincipal, а не просто как объект).Разумеется, он должен быть доступен только для текущего обрабатываемого запроса и не должен использоваться совместно со всеми другими потоками/запросами.

Прямо сейчас я назначаю свой объект CommunityPrincipal HttpContext.User в HttpModule, но это требует от меня выполнения приведения каждый раз, когда мне нужно использовать свойства объекта CommunityPrincipal, которые не определены в интерфейсе IPrincipal.

Это было полезно?

Решение

Назначение пользовательского принципала для Context.User является правильным.Надеюсь, вы делаете это в Application_AuthenticateRequest.

Что касается вашего вопроса: вы получаете доступ к объекту пользователя только со страниц ASPX?Если это так, вы можете реализовать собственную базовую страницу, содержащую для вас актерский состав.

public class CommunityBasePage : Page
{
    new CommunityPrincipal User
    {
        get { return base.User as CommunityPrincipal; }
    }
}

Затем сделайте ваши страницы наследующими от CommunityBasePage и вы сможете добраться до всех своих объектов из this.User.

Другие советы

Я бы рекомендовал вам избегать привязки ваших данных к самому потоку.Вы не можете контролировать, как asp.net использует потоки сейчас или в будущем.

Данные очень сильно привязаны к контексту запроса, поэтому они должны определяться, жить и умирать вместе с контекстом.Это как раз подходящее место для его размещения, и создание экземпляра объекта в HttpModule также уместно.

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

Вот как бы я это реализовал:

Создайте статический класс для размещения метода расширения:

public static class ContextExtensions
{
    public static CommunityPrinciple GetCommunityPrinciple(this HttpContext context)
    {
        if(HttpContext.Current.Items["CommunityPrinciple"] != null)
        {
            return HttpContext.Current.Items["CommunityPrinciple"] as CommunityPrinciple;
        }
    }
}

В вашем HttpModule просто поместите принципала в коллекцию элементов контекста, например:

HttpContext.Current.Items.Add("CommunityPrincipal", MyCommunityPrincipal); 

Это сохраняет пользовательское свойство обычного контекста в естественном состоянии, так что сторонний код, код платформы и все остальное, что вы пишете, не подвергается риску из-за того, что вы подделали обычный IPrincipal, введенный там.Экземпляр существует только во время запроса пользователя, для которого он действителен.И что самое приятное, этот метод доступен для кода, как если бы это был обычный член HttpContext....и актерский состав не нужен.

Поскольку вы уже сохранили объект в свойстве HttpContext.User, все, что вам действительно нужно для достижения цели, — это статический метод, который достигает вашей цели:

  public static class MySpecialContext
  {
    public static CommunityPrinciple Community
    {
        get
        {
           return (CommunityPrinciple)HttpContext.Current.User;
        }
    }
  }

Теперь вы можете получить CommunityPrinciple как: -

  var x = MySpecialContext.Community;

Однако, похоже, нужно приложить много усилий, чтобы избежать:

  var x = (CommunityPrinciple)Context.User;

Альтернативой может быть метод расширения HttpContext: -

  public static class HttpContextExtensions
  {
    public static CommunityPrinciple GetCommunity(this HttpContext o)
    {
      return (CommunityPrinciple)o.User;
    }
  }

Используйте это: -

  var x = Context.GetCommunity();

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

Редактировать:

Давайте на мгновение предположим, что у вас есть действительно веская причина, почему даже приведение, выполненное внутри вызываемого кода, как указано выше, по-прежнему неприемлемо (кстати, мне было бы очень интересно понять, какие обстоятельства приводят вас к такому выводу).

Еще одной альтернативой является поле ThreadStatic:

  public class MyModule : IHttpModule
  {
    [ThreadStatic]
    private static CommunityPrinciple _threadCommunity;

    public static CommunityPrinciple Community
    {
        get
        {
           return _threadCommunity;
        }
    }
    // Place here your original module code but instead of (or as well as) assigning
    // the Context.User store in _threadCommunity.
    // Also at the appropriate point in the request lifecyle null the _threadCommunity

  }

Поле, украшенное [ThreadStatic], будет иметь один экземпляр хранилища для каждого потока.Следовательно, несколько потоков могут изменять и читать _threadCommunity, но каждый из них будет работать со своим конкретным экземпляром поля.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top