2009-01-10 9 views
22

Estaba haciendo una pregunta relacionada pero arruiné el título y nadie lo entendería. Como ahora puedo hacer la pregunta con mayor precisión, decidí reformularla en una nueva pregunta y cerrar la anterior. Lo siento por eso.¿Por qué es nulo el Usuario (como en User.Identity.Name) en mi controlador base abstracto?

Entonces, lo que quiero hacer es pasar datos (el apodo de mi usuario personalizado almacenado en el archivo db) al LoginUserControl. Este inicio de sesión se representa desde la página maestra a través de Html.RenderPartial(), así que lo que realmente necesito hacer es asegurarme de que, digamos ViewData ["UserNickname"] esté presente en cada llamada. Pero no quiero poblar ViewData [ ""] UserNickname en todos y cada acción de cada controlador, por lo que decidí usar this approach y crear un controlador de base abstracta que va a hacer el trabajo para 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 a User.Identity.Name porque User ya es nulo. Este no es el caso en todos mis controladores derivados, así que esto es específico para el controlador base abstracto.

Estoy estableciendo el User.Identity.Name a través de FormsAuthentication en otro lugar del código, pero creo que este no puede ser el problema: un Usuario de User.Identity.Name puede ser nulo, pero no el Usuario en sí.

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

Respuesta

12

Supongo que el constructor base del Controlador no está llenando el Usuario, pero que solo se conocerá más adelante cuando ControllerContext esté configurado para el Controlador. Debería verificar esto en la documentación sobre el ciclo de vida de una aplicación MVC, (la que probablemente sea here, aunque podría estar un poco desactualizada ya que es para la versión de vista previa), o simplemente verificar el código fuente de MVC.

a partir del código que tengo de MVC (también una versión preliminar, 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; 
     } 
    } 

I don' t ver en una implementación de un constructor predeterminado en el código. Eso probaría que ControllerContext es nulo en el momento de la construcción.

Por lo tanto, debe ejecutar su código en otro lugar.

+7

Extraño, porque 'Usuario' es nulo pero' System.Web.HttpContext.Current.User' no lo es. MVC 1.0 –

4

¿Puede agarrar esta usando algo como:

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

O es el HttpContext siempre vacío ??

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

4

Gracias Raimond. Estaba demasiado cansado para ver lo obvio. @ Keney: 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 utilizo para decorar todas las acciones de mi controlador. Aquí está la aplicació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" }; 
     } 
    } 

voy a buscar en la manera de conseguir que el código que se ejecuta de una manera que sigue el principio DRY, ya que el uso de atributos que definitivamente sí mismo significa repetir. Tal vez algún tipo de interceptor (interesting idea) o gancho podrían ayudar.

¡Salud para eso!

+1

Recomendaría sobreescribir el método Initialize de su clase de Controlador personalizado y mover su código desde el constructor hasta allí. – jcmcbeth

0

Estoy haciendo esto en una implementación de controlador de base 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í User != null

+0

Esto se debe a que ese objeto Usuario se completa cuando se llama a la Propiedad LoggedOn. – jcmcbeth

21

La respuesta a este problema es en realidad 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 en ActionExecuting() en la clase de controlador de base (creé un atributo personalizado para él, pero simplemente reemplazar 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.

+0

Gracias por publicar cómo resolvió eso – JeremyWeir

+0

Mi placer :-) – Masterfu

0

a Masterfu: Hice algo similar con su ayuda, deseo que pueda ayudar a los visitantes posteriores. En mi caso, necesito crear repositorio de controladores para diferentes usuarios, sin embargo, en el constructor de controladores, el usuario no está listo. Así que he creado un atributo para los controladores:

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

la _repository, de hecho, no es una variable privada del controlador, pero somethign crean 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); 
     } 
    } 
} 

puse códigos de crear el repositorio de un método separado, en el caso de que otros atributos deseen utilizar Usuario (principal) antes de que se active este atributo.

0

Llamar desde un constructor es demasiado pronto en la tubería de MVC.

Al pasar el código a OnAuthorization, obtiene el usuario autorizado en un parámetro. ¡Trabajó para mi!

de su ejemplo me gustaría hacer 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; 
    } 


} 
11

La propiedad del usuario no se asigna hasta después de que el controlador ha creado una instancia, pero se puede obtener acceso temprano de su constructor:

System.Web.HttpContext.Current.User 
Cuestiones relacionadas