Почему User (как в User.Identity.Name) равен нулю в моем абстрактном базовом контроллере?

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

Вопрос

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

Итак, что я хочу сделать, это передать данные (ник моего пользовательского пользователя, сохраненный в базе данных) в LoginUserControl.Этот логин отображается с главной страницы через Html.RenderPartial() , поэтому то, что мне действительно нужно сделать, это убедиться, что, скажем, ViewData["UserNickname"] присутствует при каждом вызове.Но я не хочу заполнять ViewData["UserNickname"] в каждом действии каждого контроллера, поэтому я решил использовать такой подход и создайте абстрактный базовый контроллер, который будет выполнять эту работу за меня, вот так:

public abstract class ApplicationController : Controller
    {
        private IUserRepository _repUser;

        public ApplicationController()
        {
            _repUser = RepositoryFactory.getUserRepository();
            var loggedInUser = _repUser.FindById(User.Identity.Name); //Problem!
            ViewData["LoggedInUser"] = loggedInUser;
        }
    }

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

Пока все идет так хорошо.Теперь перейдем к проблеме:

Я не могу вызвать пользователя.Личность.Имя, потому что User уже равно нулю.Это не относится ко всем моим производящим контроллерам, так что это специфично для абстрактного базового контроллера.

Я устанавливаю User.Identity.Имя через FormsAuthentication в другом месте кода, но я думаю, что это не может быть проблемой - afaik User.Identity.Имя может быть нулевым, но не сам Пользователь.

Мне кажется, что HttpContext недоступен (поскольку также null ;-) и что я упускаю здесь простой, но важный момент.Кто-нибудь может дать мне несколько советов?Я был бы вам очень признателен.

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

Решение

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

из имеющегося у меня кода MVC (также предварительная версия, но это должно быть нормально):(В Контроллере)

 public IPrincipal User {
            get {
                return HttpContext == null ? null : HttpContext.User;
            }
        }

...

public HttpContextBase HttpContext {
        get {
            return ControllerContext == null ? null : ControllerContext.HttpContext;
        }
    }

Я не вижу в коде реализации конструктора по умолчанию.Это доказало бы, что ControllerContext равен null на момент построения.

Поэтому вам следует выполнить свой код где-нибудь в другом месте.

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

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

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

Теперь все работает как положено, и у меня нет повторного кода.

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

System.Web.HttpContext.Current.User

Можете ли вы взять это, используя что-то вроде:

HttpContext currentContext = HttpContext.Current;
string userName = currentContext.User.Identity.Name;

Или HttpContext всегда пуст ??

Не могли бы вы установить httpContext через конструктор абстрактного класса? и использовать это таким образом?

Спасибо, Раймонд. Я был слишком уставшим, чтобы видеть очевидное. @ Кини: Да, контекст всегда нулевой. Раймон указал на причину. В любом случае, я тоже не понимаю, почему: -)

Мое текущее рабочее решение (хотя и не то, что я хотел) - это Атрибут, который я использую для украшения всех своих действий контроллера. Вот реализация:

public class MasterPageDataAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            base.OnActionExecuting(filterContext);
            IUserRepository _repUser = RepositoryFactory.getUserRepository();
            IPrincipal siteUser = filterContext.Controller.ControllerContext.HttpContext.User;
            User loggedInUser = null;

            if (siteUser == null || siteUser.Identity.Name == null)
            {
                //do nothing
            }
            else
            {
                loggedInUser = _repUser.findUserById(siteUser.Identity.Name);
            }
            filterContext.Controller.ViewData["LoggedInUser"] = loggedInUser ?? new User { Nickname = "Guest" };
        }
    }

Я буду изучать, как заставить этот код выполняться в соответствии с принципом DRY, поскольку использование атрибутов для этого определенно означает повторение себя. Может быть, какой-то перехватчик -interceptors.aspx "rel =" nofollow noreferrer "> интересная идея ) или хук может помочь.

Приветствия для этого.

Я делаю это в реализации базового контроллера, и она работает как положено.

public abstract class BaseController : Controller
{
    public bool LoggedOn
    {
        get { return User.Identity.IsAuthenticated; }
    }
}

Это всегда возвращает true или false для меня, поэтому Пользователь! = null

Мастерфу: Я сделал что-то похожее с вашей помощью, хочу, чтобы это помогло последним посетителям. В моем случае мне нужно создать репозиторий контроллеров для разных пользователей, но в конструкторе контроллеров (основной) пользователь не готов. Поэтому я создал атрибут для контроллеров:

[CreateRepositoryByUser]
public class MFCController : Controller
{
    protected MFCRepository _repository
    {
        get { return ViewData["repository"] as MFCRepository; }
    }
...

_repository, на самом деле, не является частной переменной контроллера, а создается как-то атрибутом:

public class CreateRepositoryByUser : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        CreateRepository(filterContext);
    }

    public static void CreateRepository(ActionExecutingContext filterContext)
    {
        if (filterContext.Controller.ViewData["repository"] == null)
        {
            filterContext.Controller.ViewData["repository"] =
                MFCRepository.CreateMFCRepository(filterContext.Controller.ControllerContext.HttpContext.User);
        }
    }
}

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

Вызов из конструктора слишком рано в конвейере MVC.

Перемещая код в OnAuthorization, вы получаете авторизованного пользователя в параметре. Работал на меня!

Из вашего примера я бы сделал что-то вроде этого:

public abstract class ApplicationController : Controller {
    private IUserRepository _repUser;

    protected override void OnAuthorization(AuthorizationContext filterContext)
    {
        _repUser = RepositoryFactory.getUserRepository();
        var loggedInUser = _repUser.FindById(filterContext.HttpContext.User.Identity.Name); //Problem!
        ViewData["LoggedInUser"] = loggedInUser;
    }


}

Вставьте IPrincipal , если вам нужен Пользователь в конструкторе.

 // startup.cs
 // Inject IPrincipal
 services.AddTransient<IPrincipal>(provider => provider.GetService<IHttpContextAccessor>().HttpContext.User);

Затем добавьте как IPrincipal в свой конструктор. Обратите внимание, что он гарантированно будет ClaimsPrincipal в ASPNET, потому что это HttpContext.User .

Аналогичный вопрос

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