2015-03-06 3 views
2

Я создаю приложение MVC 5 и столкнулся со следующей проблемой: я хочу показать пользователю пункт меню после входа пользователя, если у пользователя есть соглашение со мной.MVC 5 - установить переменную сеанса при входе в систему

Я хочу, чтобы установить переменную сеанса в момент входа пользователя в систему как:

Session["HasAgreement"] = Agreement.HasAgreement(userId); 

, а затем в моем файле _Layout.cshtml, где я построю свое меню сделать что-то вроде:

@if (Session["HasAgreement"] == "True") 
{ 
    <li>@Html.ActionLink("Agreement", "Agreement", "Home")</li> 
} 

Моя проблема возникает в AccountController, где я добавил логику стандартного Вход действий:

public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) 
{ 
    if (!ModelState.IsValid) 
    { 
     return View(model); 
    } 

    var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false); 
    switch (result) 
    { 
     case SignInStatus.Success: 
      var userId = User.Identity.GetUserId(); 
      Session["HasAgreement"] = Agreement.HasAgreement(userId); 
      return RedirectToLocal(returnUrl); 
     case SignInStatus.LockedOut: 
      return View("Lockout"); 
     case SignInStatus.RequiresVerification: 
      return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe }); 
     case SignInStatus.Failure: 
     default: 
      ModelState.AddModelError("", "Invalid login attempt."); 
      return View(model); 
    } 
} 

Это стандартный вход MVC 5 - за исключением того, что я добавил две строки сразу после «case SignInStatus.Success:», где я пытаюсь получить userId, а затем установить переменную Session.

Моя проблема заключается в том, что в этот момент времени пользователь не аутентифицирован (я думал, что это произошло в SignInManager выше).

Как установить переменную сеанса сразу после входа пользователя в систему?

+0

возможно дубликат [Asp.net идентичность: User.Identity.GetUserId() всегда утративший User.Identity.IsAuthenticated является ложным во все дни] (http://stackoverflow.com/questions/25439275/asp-net-identity-user-identity-getuserid-is-always-null-and-user-identity-is) – st4hoo

+0

Да, это правда! Это дубликат. Я просто не мог найти ответ, когда искал раньше! Спасибо. – olf

+0

st4hoo, если вы оставите ответ, я могу отметить его как правильно. – olf

ответ

1

Ваша проблема не в том, что вы обновляете переменную сеанса, но какую версию сеанса имеет ваша страница макета.

сеанс не является лучшим вариантом для передачи данных между видами. попробуйте ViewBag

(хотя вы всегда должны пытаться использовать ViewModel, где это возможно!) (и вы можете использовать сессии, а также для следующей загрузки страницы)

+0

ahh, но его в макете – Ewan

+0

Sippy, я согласен, что в большинстве случаев я бы использовал модель представления. В этом специальном случае я буду использовать переменную сеанса, поскольку она используется во всем приложении (для пользователя, зарегистрированного в системе) (_Layout.cshtml используется всеми моими представлениями) - и я не хотел бы заполнять мои просмотреть модель для каждого вида/страницы. – olf

+0

У меня была аналогичная проблема, переменная сеанса странная и не будет обновляться с вашим значением, когда макет пытается ее прочитать – Ewan

2

Новая сессия не не установлен, пока вы нажмите следующее действие. Это распространенная проблема с попыткой использовать результат для чего угодно, кроме перенаправления. Лучшим способом действий было бы использовать результат для перенаправления на другое действие, в котором вы сможете получить доступ к сеансу.

Предполагая, когда ваш вход пользователя в они идут к «приборной панели», это может выглядеть примерно так:

SignInStatus.Success случае:

case SignInStatus.Success: 
    return RedirectToAction("Dashboard"); 

Если требуется возможность вернуться к многочисленные действия, вы можете вернуть имя действия вместо URL-адреса и просто сделать RedirectToAction(returnAction). Очевидно, что если вам нужно также указать контроллер, вам также нужно разместить returnController.

Dashboard действие:

[Authorize] 
public ActionResult Dashboard() { 
    var userId = User.Identity.GetUserId(); 

    Session["HasAgreement"] = Agreement.HasAgreement(userId); 

    return View(); 
} 
+0

Я не думаю, что ваше первое предложение истинно ... 'RedirectToLocal' (используется в новейшая Identity, а также то, на что ссылается OP) - это собственный 'ActionResult' в' AccountController'. Поэтому, по вашему определению и примеру, он должен уметь помещать 'Session [" HasAgreement "] = Соглашение. HasAgreement (userId);' в 'RedirectToLocal' и работать ли он? Я имею дело с чем-то почти идентичным, и это не работает для меня. – SumNone

1

Я не уверен, где ваш Agreement объект прибывает из, но у вас есть доступ к User собственности в View, чтобы вы могли потенциально сделать что-то вроде этого:

_Layout.cshtml

@if (Agreement.HasAgreement(User.Identity.GetUserId())) 
{ 
    <li>@Html.ActionLink("Agreement", "Agreement", "Home")</li> 
} 

это также предполагает, что HasAgreement возвращает bool, который, если он не делает, это действительно надо.

+0

Да Соглашение. HasAgreement возвращает bool. И вместо повторного вызова метода я хочу только один раз его вызывать (поскольку есть некоторые тяжелые db-вызовы, которые я хочу избегать делать больше одного раза). Поэтому я сохраняю эту единственную переменную в переменной сеанса «раз и навсегда». – olf

+0

@olf, тогда вы должны кэшировать внутри метода. Сохраните словарь идентификаторов с bool и запишите это один раз, прочитайте много. –

0

В качестве опции:

Добавить новый частичный вид - назовем его Agreement.

@model bool 
@if (Model) 
{ 
    <li>@Html.ActionLink("Agreement", "Agreement", "Home")</li> 
} 

Добавить новое действие вашего, скажем, Account контроллер, назовем его Agreement

public PartialViewResult Agreement(){ 
    var userId = User.Identity.GetUserId(); 
    bool hasAgreement = Agreement.HasAgreement(userId); // This will be your model 
    return PartialView("Agreement", hasAgreement); 
} 

И в макете сделать:

@Html.RenderAction("Agreement", "Account") 
+0

Еще одно хорошее решение, но, как уже было сказано выше, я действительно хочу сохранить его в переменной сеанса прямо при входе в систему, чтобы не постоянно вызывать метод Agreement.HasAgreement. – olf

0

У меня точно такой же вопрос и, по всей видимости используйте ту же версию Identity, что и вы (OP). Я попробовал то, что вы сделали (до обнаружения этого), а также последовал совет Sippy и положить его в RedirectToLocalActionResult в AccountController (который не работает):

В то же время, я положил его в global.asax .cs под:

public void Profile_OnMigrateAnonymous(object sender, ProfileMigrateEventArgs args) 
{ 
    ...//other code (or not) on migrate anonymous user to authenticated 
    Session["HasAgreement"] = Agreement.HasAgreement(userId); 
} 

Я также создал эту пустоту в моем Global.asax.cs (уловы истекли сессии, но до сих пор вошли в системе):

void Session_Start(object sender, EventArgs e) 
{ 
    Session["HasAgreement"] = Agreement.HasAgreement(userId); 
} 

Он работает, а также обновляет, если истекает сеанс. Однако одним из недостатков этого является то, что переменная сеанса не будет обновляться, когда пользователь выйдет из системы (через идентификатор). Тем не менее, я также попытался разместить расширение моего клиента в LogOffActionResult после AuthenticationManager.SignOut();, и он не работает.

Мне нужна моя переменная сеанса для обновления после выхода из системы, поэтому это не будет моим окончательным решением, но это может быть достаточно для вас?

Я вернусь и обновлю это, если найду лучший способ, но сейчас это достаточно хорошо, и я перехожу на мой список TODO.

UPDATE:

Чтобы поймать событие выхода из системы пользователя, я использовал идею Sippy и изменил мой LogOffActionResult в AccountController к этому:

 // 
     // POST: /Account/LogOff 
     [HttpPost] 
     [ValidateAntiForgeryToken] 
     public ActionResult LogOff() 
     { 
      AuthenticationManager.SignOut(); 
      return RedirectToAction("SetSessionVariables", "Account"); 
     } 

     [AllowAnonymous] 
     public ActionResult SetSessionVariables() 
     { 
      Session["HasAgreement"] = Agreement.HasAgreement(userId); 
      return RedirectToAction("Index", "Home"); 
     } 

Я думаю, что это включает в себя все вход/выход из системы/сессионных сценариев. Я мог бы увидеть, могу ли я включить то, что я сделал для LogOff, в успешную переадресацию входа в систему (более того, чтобы увидеть, когда пользователь фактически «аутентифицирован», чем что-либо).

1

Это может не работать во всех случаях, но я нашел самый простой способ установить переменную сеанса в Login, чтобы переместить логику в действие контроллера, возвращаемое RedirectToLocal(). В моем случае я сделал новую точку входа для входа в приложение под названием «Main», у которой есть собственный view/controller. При первом входе в систему пользователь всегда перенаправляется сюда, поэтому он гарантирует, что мои данные сеанса будут установлены.

Сначала я изменил RedirectToLocal() в AccountController:

private ActionResult RedirectToLocal(string returnUrl) 
{ 
    if (Url.IsLocalUrl(returnUrl)) 
    { 
     return Redirect(returnUrl); 
    } 
    //This is the template default 
    //return RedirectToAction("Index", "Home");   

    //This is my new entry point. 
    return RedirectToAction("Index", "Main");  } 

Внутри моего MainController я могу получить доступ к User объекту и установить мои данные:

// GET: Main 
public ActionResult Index() 
{ 
    ApplicationUser sessionuser = db.Users.Find(User.Identity.GetUserId()); 
    Session.Add("UserName", sessionuser.UserName); 
    return View(); 
} 

И для хорошей меры я вытирал сессию данные по возможности выхода из системы:

public ActionResult LogOff() 
{ 
    Session.RemoveAll(); //Clear all session variables 
    //...    
} 
Смежные вопросы