Pergunta

Eu estava fazendo uma pergunta relacionada, mas errei o título para cima e ninguém iria entender. Desde que eu sou capaz agora de fazer a pergunta mais precisamente, decidi reformulá-lo em uma nova pergunta e fechar o antigo. Desculpe por isso.

Então, o que eu quero fazer é passar dados (apelido do meu usuário personalizada como armazenados no db) ao LoginUserControl. Este login é renderizado da página mestra via Html.RenderPartial (), então o que eu realmente preciso fazer é ter certeza de que, dizem ViewData [ "UserNickname"] está presente em cada chamada. Mas eu não quero para preencher ViewData [ "UserNickname"] em cada ação de cada controlador, então eu decidi uso esta abordagem e criar um controlador de base abstrata que irá fazer o trabalho para mim, assim:

public abstract class ApplicationController : Controller
    {
        private IUserRepository _repUser;

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

Desta forma, qualquer que seja o meu derivando controlador faz, as informações do usuário já estará presente.

Até agora, tão bom. Agora para o problema:

Eu não posso chamar User.Identity.Name porque User já é nulo. Este não é o caso em todos os meus controladores decorrentes, por isso este é específico para o controlador de base abstrato.

Eu estou definindo o User.Identity.Name via FormsAuthentication em outro lugar no código, mas acho que isso não pode ser o problema -. Afaik User.Identity.Name pode ser nulo, mas não é em si usuário

Parece-me que o HttpContext não está disponível (desde igualmente nula ;-) e que estou faltando um ponto importante aqui simples ainda. Alguém pode me dar algumas dicas? Eu realmente aprecio isso.

Foi útil?

Solução

Meu palpite seria que construtor base do Controlador não está preenchendo o usuário, mas que só é conhecido depois, quando o ControllerContext está definido para o Controller. Você deve verificar isso na documentação sobre o ciclo de vida de uma aplicação MVC, (o aqui provavelmente irá fazer, embora possa ser um pouco fora de data, já que é para a versão de pré-visualização), ou apenas verificar o código-fonte do MVC.

a partir do código que eu tenho de MVC (também uma versão preview, mas isso deve ser fino): (Em Controller)

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

...

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

Não vejo en uma implementação de um construtor padrão no código. Isso provaria que o ControllerContext é nulo no momento da construção.

Portanto, você deve executar o código em outro lugar.

Outras dicas

A resposta para este problema é realmente muito simples. Eu não posso executar o código a partir do construtor por razões apontadas por Raimond, mas posso fazê-lo fora do construtor.

Então o que eu fiz foi substituir OnActionExecuting () na classe controlador de base (eu criei um atributo personalizado para ele, mas apenas substituindo o método também trabalho deve) e, em seguida, fazer a minha pesquisa de usuário de lá.

Agora, ele funciona como esperado e eu não ter repetido código.

A propriedade usuário não for atribuído até que o controlador foi instanciado, mas você pode ganhar acesso antecipado a partir do seu construtor com:

System.Web.HttpContext.Current.User

Você pode agarrar esta usando algo como:

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

Ou é o HttpContext sempre esvaziar ??

Você poderia definir o httpContext através do construtor da classe abstrata? e usá-lo dessa forma?

Graças Raimond. Eu estava muito cansado para ver o óbvio. @Keeney: Sim, o contexto é sempre nulo. Raimond apontou o porquê. Obrigado de qualquer forma, eu não vejo porque também: -)

A minha solução de trabalho atual (embora não o que eu queria) é um atributo que eu uso para decorar todas as ações meu controlador. Aqui é a implementação:

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

Eu vou estar olhando para como obter esse código executado de uma forma que segue o princípio de DRY, já usando atributos para que definitivamente significa repetir-se. Talvez algum tipo de interceptor ( interessante ideia ) ou gancho pode ajudar.

Felicidades para isso.

Estou fazendo isso em uma implementação BaseController e funciona como esperado.

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

Isso sempre retorna verdadeiro ou falso para mim, então User != null

para Masterfu: Eu fiz algo semelhante com a sua ajuda, desejo que pode ajudar últimos visitantes. No meu caso, eu preciso criar reposiotry de controladores para diferentes usuários, ainda no construtor de controladores, (principal) Utilizador não está pronto. Então, eu criei um atributo para controladores:

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

o _repository, na verdade, não é uma variável privada de controlador, mas somethign criar pelo atributo:

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

Eu coloquei códigos de criar o repositório em um método individual, num caso de que outros atributos pode querer usar (principal) do usuário antes de este atributo a ser acionado.

Chamar de um construtor é muito cedo no pipeline MVC.

Mover código para OnAuthorization, você começa usuário autorizado em um parâmetro. Trabalhou para mim!

A partir do seu exemplo, eu faria algo assim:

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


}

Inject IPrincipal Se você precisa User no construtor.

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

Em seguida, adicione como IPrincipal em seu construtor. Note que é garantido para ser ClaimsPrincipal com ASPNET -. Porque é isso que HttpContext.User é

pergunta homóloga

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top