Почему 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;-) и что я пропустил простой, но важный момент здесь. Может ли кто-нибудь дать мне несколько советов? Я был бы очень признателен.
Ответы
Ответ 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
.
Подобный вопрос