Por qué es Usuario (como en User.Identity.Name) null en mi controlador base abstracto?


Estaba haciendo una pregunta relacionada, pero arruinó el título y nadie lo entendería. Dado que ahora puedo formular la pregunta con mayor precisión, decidí reformularla en una nueva pregunta y cerrar la anterior. Lo siento por eso.

Así que lo que quiero hacer es pasar datos (el apodo de mi usuario personalizado almacenado en la base de datos) al LoginUserControl. Este inicio de sesión se procesa desde la página maestra a través de Html.RenderPartial(), así que lo que realmente necesito hacer es ViewData ["UserNickname"] está presente en cada llamada. Pero no quiero rellenar ViewData ["UserNickname" ] en todas y cada una de las acciones de cada controlador, así que decidí usar este enfoque y crear un controlador base abstracto que hará el trabajo por mí, así:

public abstract class ApplicationController : Controller
    {
        private IUserRepository _repUser;

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

De esta manera, cualquiera que sea mi Controlador derivado, la información del usuario ya estará presente.

Hasta ahora, todo bien. Ahora para el problema:

No puedo llamar User.Identity.Name porque User es ya null. Este no es el caso en todos mis controladores derivados, por lo que es específico para el controlador base abstracto.

Estoy estableciendo la User.Identity.Name vía FormsAuthentication en otro lugar en el código, pero creo que este no puede ser el problema - afaik User.Identity.Name puede ser null, pero no el propio Usuario.

Me parece que el HttpContext no está disponible (ya que también null ;-) y que me falta un punto simple pero importante aquí. ¿Alguien puede darme alguna pista? Me realmente lo apreciaría.

Author: Masterfu, 2009-01-10

8 answers

Mi conjetura sería que el constructor base del Controlador no está llenando el Usuario, pero que solo se sabe más tarde cuando el ControllerContext se establece para el Controlador. Usted debe comprobar esto en la documentación sobre el ciclo de vida de una aplicación MVC, (el aquí probablemente hará, aunque podría ser un poco desactualizado ya que es para la versión de vista previa), o simplemente comprobar el código fuente de MVC.

Del código que tengo de MVC (también una versión previa, pero que debería estar bien): (En el Controlador)

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

...

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

No veo una implementación de un constructor predeterminado en el código. Eso demostraría que el ControllerContext es nulo en el momento de la construcción.

Así que deberías ejecutar tu código en otro lugar.

 12
Author: Raymond Roestenburg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-10 09:34:38

La respuesta a este problema es bastante simple. No puedo ejecutar el código desde dentro del constructor por razones señaladas por Raimond, pero puedo hacerlo fuera del constructor.

Así que lo que hice fue anular OnActionExecuting() en la clase controller base (Creé un atributo personalizado para ella, pero simplemente anular el método también debería funcionar) y luego hacer mi búsqueda de usuario desde allí.

Ahora funciona como se esperaba y no tengo código repetido.

 21
Author: Masterfu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-19 11:42:58

La propiedad de usuario no se asigna hasta después de que se haya instanciado el Controlador, pero puede obtener acceso anticipado desde su constructor con:

System.Web.HttpContext.Current.User
 13
Author: Austin Hummel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-26 19:14:57

Puedes agarrar esto usando algo como:

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

¿O el HttpContext siempre está vacío??

¿Podría establecer el HttpContext a través del constructor de la clase abstracta? y usarlo de esta manera?

 4
Author: keeney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-10 09:01:06

Gracias Raimond. Estaba demasiado cansado para ver lo obvio. @Keeney: Sí, el contexto siempre es nulo. Raimond señaló por qué. Gracias de todos modos, no vi por qué también: -)

Mi solución de trabajo actual (aunque no es lo que quería) es un Atributo que uso para decorar todas mis acciones de controlador. Aquí está la implementación:

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

Buscaré cómo ejecutar ese código de una manera que siga el principio DRY, ya que usar atributos para eso definitivamente significa repetir uno mismo. Tal vez algún tipo de interceptor (idea interesante) o gancho podría ayudar.

Salud por eso.

 4
Author: Masterfu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-10 15:54:16

Estoy haciendo esto en una implementación de basecontroller y funciona como se esperaba.

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

Esto siempre devuelve verdadero o falso para mí así que User != null

 0
Author: Schotime,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-01-11 22:23:14

A Masterfu: Hice algo similar con su ayuda, deseo que puede ayudar a los últimos visitantes. En mi caso, necesito crear reposicionamiento de controladores para diferentes usuarios, sin embargo, en el constructor de controladores, el Usuario (principal)no está listo. Así que creé un atributo para controladores:

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

El _repository, de hecho, no es una variable privada del controlador, sino que algo crea por el 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);
        }
    }
}

Pongo los códigos de creación del repositorio en un método separado, en caso de es posible que otros atributos quieran usar (principal)User antes de que se active este atributo.

 0
Author: cheny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-17 02:55:37

Llamar desde un constructor es demasiado pronto en la canalización MVC.

Al mover el código a OnAuthorization, se obtiene el usuario autorizado en un parámetro. ¡Funcionó para mí!

De tu ejemplo haría algo como esto:

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


}
 0
Author: Simon Novak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-16 15:08:26