Подтвердить что ты не робот

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

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

Итак, я хочу передать данные (мой пользовательский псевдоним, хранящийся в db) в 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.Identity.Name, потому что User уже null. Это не так во всех моих получающих контроллерах, поэтому это характерно для абстрактного базового контроллера.

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

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

4b9b3361

Ответ 1

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

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

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

...

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

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

Итак, вы должны выполнить свой код где-то еще.

Ответ 2

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

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

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

Ответ 3

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

System.Web.HttpContext.Current.User

Ответ 4

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

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

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

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

Ответ 5

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

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

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, поскольку использование атрибутов для этого означает, что это означает повторение. Может быть, какой-то перехватчик (интересная идея) или крючок может помочь.

Приветствия за это.

Ответ 6

Я делаю это в реализации basecontroller и работает как ожидалось.

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

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

Ответ 7

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

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

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

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

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

Ответ 8

Вызов из конструктора слишком скоро в конвейере 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;
    }


}

Ответ 9

IPrincipal если вам нужен User в конструкторе.

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

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

Подобный вопрос