Создание "окружающего контекста" (UserContext) для ASP.NET приложение, использующее статический завод Func

я узнал, что мне нужны текущие зарегистрированные пользовательские данные почти в каждом классе (контроллеры, просмотр, HTML-помощники, службы и так далее). Поэтому я подумал о создании "окружающего контекста" вместо непосредственного ввода IUserService или пользователя.

мой подход выглядит примерно так.

public class Bootstrapper
{
    public void Boot()
    {
        var container = new Container();
        // the call to IUserService.GetUser is cached per Http request
        // by using a dynamic proxy caching mechanism, that also handles cases where we want to 
        // invalidate a cache within an Http request
        UserContext.ConfigureUser = container.GetInstance<IUserService>().GetUser;
    }
}

public interface IUserService
{
    User GetUser();
}

public class User
{
    string Name { get; set; }
}

public class UserContext : AbstractFactoryBase<User>
{
    public static Func<User> ConfigureUser = NotConfigured;

    public static User ActiveUser { get { return ConfigureUser(); } }
}

public class AbstractFactoryBase<T>
{
    protected static T NotConfigured()
    {
        throw new Exception(String.Format("{0} is not configured", typeof(T).Name));
    }
}

пример использования:

public class Controller
{
     public ActionResult Index()
     {
         var activeUser = UserContext.ActiveUser;
         return View();
     }
}

мой подход правильный, или я что-то пропустил? У вас есть лучшие решения в возражаете?

обновление:

подробнее о пользователей класс:

public class User
{
   string Name { get; set; }
   bool IsSuperUser { get; set;}
   IEnumerable<AzManOperation> Operations { get; set}
}

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

public class BaseController : Controller
{
    private readonly IUserService _userService;

    BaseControler(IUserService userService)
    {
        _userService = userService
    }

    public User ActiveUser
    {
        get { return _userService.GetUser(); }
    }
}

на вид мы проверяем операции, чтобы показать только кнопку редактирования или удаления, если пользователь имеет на это право. Представление никогда не использует DependencyResolver, но ViewBag или ViewModel. Мой идея здесь заключается в реализации пользовательской ViewBasePage и предоставлении свойства ActiveUser, так что представления имеют легкий доступ.

на вспомогательные методы HTML приложения мы визуализируем элементы управления в зависимости от IsSuperUser и операций (передача объекта пользователя или использование DependencyResolver).

на Классы нам тоже нужны эти свойства. Например, чтобы решить, является ли корзина действительной или нет (проверьте, разрешено ли пользователю покупать статьи, которые не находятся в стандартный список.) Таким образом, класс обслуживания зависит от IUserService, а вызов GetUser().

на Фильтры Действий чтобы заставить пользователя изменить свой пароль (только если это не суперпользователь и пользователь.ForcePasswordChange это правда). Здесь мы используем DependencyResolver.

мое желание-иметь более простой способ получить объект пользователя, а не использовать DependencyResolver.Текущий.GetService().GetUser () или использование таких вещей, как ViewBag.ActiveUser = User. Объект User-это объект, который почти везде необходимо проверить разрешения или тому подобное.

3 ответов


в представлениях мы проверяем операции, чтобы показать только кнопку редактирования или удаления, если пользователь имеет на это право.

представление не должно выполнять эту проверку. Контроллер должен вернуть модель представления в представление, содержащее логические свойства, указывающие, должны ли эти кнопки быть видимыми. Возвращение bool с IsSuperUser уже движется к много knownledge в поле зрения. Представление не должно знать, что оно должно показывать определенную кнопку для super user: это зависит от контроллера. В представлении должно быть только указано, что отображать.

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

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

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


это MVC, верно?

вы изобретаете колесо.

добавьте этот метод в свой глобальный.асакс.cs:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    var authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    if (authCookie != null)
    {
        var ticket = FormsAuthentication.Decrypt(authCookie.Value);
        var user = ticket.Name;
        var identity = new GenericIdentity(user, "Forms");
        var principal = new GenericPrincipal(identity, null);
        Context.User = principal;
    }
}

в этом примере показана проверка подлинности форм, которую можно удалить при использовании другого механизма. Ключ в этих трех строках:--4-->

    var identity = new GenericIdentity(user, "Forms");
    var principal = new GenericPrincipal(identity, null);
    Context.User = principal;

GenericIdentity и GenericPrincipal могут быть заменены чем угодно, если они реализуют (тривиальные) интерфейсы IIdentity и IPrincipal. Вы можете создать свой собственный реализации этих классов с любыми дополнительными свойствами, которые вам нужны.

затем вы можете получить доступ к аутентифицированному пользователю из всех перечисленных вами вещей-контроллеров,представлений и т. д. - через свойство HttpContext.Текущий.Пользователь (который является статическим).

Если вы создали свою собственную реализацию IPrincipal вы можете просто привести эту ссылку на свой пользовательский тип.

вы заметите, что IPrincipal имеет метод под названием IsInRole, поэтому вы скажи:

if (HttpContext.Current.User.IsInRole("SuperUser"))

TL; DR - вы что-то перегружаете ASP.NET уже решил, и у меня была бы аневризма, если бы я увидел типы, которые вы предлагаете в производственном приложении.


Я думаю, что самым простым и доступным решением является создание статического класса CurrentUserProvider, который имеет только один метод Get (HttpContextBase), который возвращает текущего пользователя, за сценой вы можете использовать DependencyResolver для получения службы, которая фактически возвращает пользователя. Затем, когда вам нужен CurrentUser, вы можете вызвать CurrentUserProvider.Get (context) и сделайте любую пользовательскую логику, которую вам нужно выполнить.

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

вы можете использовать то же свойство впрыска для фильтров тоже.

теперь, оставшиеся два-вид и помощник. Для представления можно создать специальный базовый класс, наследуемый от WebViewPage / ViewPage, и использовать IViewActivator для внедрения службы, то же самое относится к помощникам, создавать помощники, наследуемые от системных помощников, и использовать их в базовых контроллерах и представлениях.

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

поэтому мое предложение-пойти с первым.