htmlHelper.ViewContext.HttpContext.Session is null - asp.net-mvc

I am trying to use an HtmlHelper in an MVC project that I'm working on but am hung up on this problem. Why is Session null when I try to call the helper method in my nav/menu partial on page load? It works anywhere else I use it in the project but when I try and use it in my layout or menu partial I get this null reference exception.
public static class HtmlHelpers
{
public static ApplicationUser UserInformation(this HtmlHelper htmlHelper)
{
var httpContext = htmlHelper.ViewContext.HttpContext;
if (httpContext.Session["CurrentUser"] == null)
{
// Get user information
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext());
var applicationUser = userManager.FindById(httpContext.user.Identity.GetUserId());
httpContext.Session["CurrentUser"] = applicationUser;
}
return (ApplicationUser) httpContext.Session["CurrentUser"];
}
}

Try System.Web.HttpContext.Current rather than htmlHelper.ViewContext.HttpContext.
Example :
var httpContext = System.Web.HttpContext.Current;
if (httpContext.Session["CurrentUser"] == null)
{
// Do somthing.
}
You can get more information about session in here.
OR
public static ApplicationUser UserInformation(this HtmlHelper htmlHelper,HttpContextBase httpContext)
{
if (httpContext.Session["CurrentUser"] == null)
{
}
}
You can get more information about session

Related

System.ArgumentNullException: Value cannot be null - Umbraco HTTPContext on save and publish

source: https://gist.github.com/sniffdk/7600822
The following code is run by an activity outside of an http request, so i need to mock the http context.
I have mocked the http context like so:
public class GetUmbracoServiceMockedHttpContext : IGetUmbracoService
{
private UmbracoHelper umbracoHelper;
public T GetService<T>()
where T : IService
{
UmbracoContext context = UmbracoContext.Current;
if (context == null)
{
var dummyHttpContext = new HttpContextWrapper(new HttpContext(new SimpleWorkerRequest("blah.aspx", "", new StringWriter())));
context = UmbracoContext.EnsureContext(
dummyHttpContext,
ApplicationContext.Current,
new WebSecurity(dummyHttpContext, ApplicationContext.Current),
UmbracoConfig.For.UmbracoSettings(),
UrlProviderResolver.Current.Providers,
false);
}
var serviceTypeProperty = context.Application.Services
.GetType()
.GetProperties()
.SingleOrDefault(x => x.PropertyType == typeof(T));
if (serviceTypeProperty == null)
{
return default(T);
}
return (T)serviceTypeProperty
.GetValue(context.Application.Services);
}
}
I inject this IGetUmbracoService service into a controller and call:
service.GetService<IContentService>().SaveAndPublishWithStatus(item);
... The following error occurs.
System.ArgumentNullException: Value cannot be null. Parameter name:
httpContext at System.Web.HttpContextWrapper..ctor(HttpContext
httpContext) at
Umbraco.Web.SingletonHttpContextAccessor.get_Value() at
Umbraco.Web.RequestLifespanMessagesFactory.Get() at
Umbraco.Core.Services.ContentService.SaveAndPublishDo(IContent
content, Int32 userId, Boolean raiseEvents) at
Umbraco.Core.Services.ContentService.Umbraco.Core.Services.IContentServiceOperations.SaveAndPublish(IContent
content, Int32 userId, Boolean raiseEvents) at
Umbraco.Core.Services.ContentService.SaveAndPublishWithStatus(IContent
content, Int32 userId, Boolean raiseEvents)
How do i mock the http context without using the frowned upon HttpContext.Current = ...?
I assume the relevant issue comes from:
RequestLifespanMessagesFactory.cs
which in turn is calling an implementation of this:
SingletonHttpContextAccessor.cs
I did some work with Umbraco, running it from a console app and then using the Umbraco API to call into Umbraco.
I believe I based it on this project: https://github.com/sitereactor/umbraco-console-example
Might be useful.
Thanks user369142. This is what ended up working:
I also had to make sure that i was not raising any events on the SaveandPublish calls... as the HttpContext expects there to be messages registered in the context but we do not mock any... If you make sure raise events is false, it skips over the code that cares about that.
public class CustomSingletonHttpContextAccessor : IHttpContextAccessor
{
public HttpContextBase Value
{
get
{
HttpContext context = HttpContext.Current;
if (context == null)
{
context = new HttpContext(new HttpRequest(null, "http://mockurl.com", null), new HttpResponse(null));
}
return new HttpContextWrapper(context);
}
}
}
public class CustomRequestLifespanMessagesFactory : IEventMessagesFactory
{
private readonly IHttpContextAccessor _httpAccessor;
public CustomRequestLifespanMessagesFactory(IHttpContextAccessor httpAccessor)
{
if (httpAccessor == null)
{
throw new ArgumentNullException("httpAccessor");
}
_httpAccessor = httpAccessor;
}
public EventMessages Get()
{
if (_httpAccessor.Value.Items[typeof(CustomRequestLifespanMessagesFactory).Name] == null)
{
_httpAccessor.Value.Items[typeof(CustomRequestLifespanMessagesFactory).Name] = new EventMessages();
}
return (EventMessages)_httpAccessor.Value.Items[typeof(CustomRequestLifespanMessagesFactory).Name];
}
}
public class CustomBootManager : WebBootManager
{
public CustomBootManager(UmbracoApplicationBase umbracoApplication)
: base(umbracoApplication)
{
}
protected override ServiceContext CreateServiceContext(DatabaseContext dbContext, IDatabaseFactory dbFactory)
{
//use a request based messaging factory
var evtMsgs = new CustomRequestLifespanMessagesFactory(new CustomSingletonHttpContextAccessor());
return new ServiceContext(
new RepositoryFactory(ApplicationCache, ProfilingLogger.Logger, dbContext.SqlSyntax, UmbracoConfig.For.UmbracoSettings()),
new PetaPocoUnitOfWorkProvider(dbFactory),
new FileUnitOfWorkProvider(),
new PublishingStrategy(evtMsgs, ProfilingLogger.Logger),
ApplicationCache,
ProfilingLogger.Logger,
evtMsgs);
}
}
public class CustomUmbracoApplication : Umbraco.Web.UmbracoApplication
{
...
protected override IBootManager GetBootManager()
{
return new CustomBootManager(this);
}
...
}

Trouble retaining CustomPrincipal type in HttpContext

I have an MVC app which I'm having trouble ex[posing a custom principal to my views. It has the following classes that help me manage auth cookies.
public class AuthenticationManager
{
public void SetAuthCookie(UserViewModel user)
{
var serializeModel = new CustomPrincipalSerializeModel
{
Id = user.UserId,
Email = user.Email,
Name = user.Name
};
var serializer = new JavaScriptSerializer();
var customPrincipal = customPrincipalMapper.Convert(serializeModel);
var httpContext = ContextHelper.GetHttpContextBase();
httpContext.User = customPrincipal;
var userData = serializer.Serialize(serializeModel);
var authTicket = new FormsAuthenticationTicket(1, serializeModel.Email, DateTime.Now, DateTime.Now.AddYears(5), false, userData);
var encTicket = FormsAuthentication.Encrypt(authTicket);
var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket);
httpContext.Response.Cookies.Add(authCookie);
}
}
public static class ContextHelper
{
public static HttpContextBase GetHttpContextBase()
{
return new HttpContextWrapper(HttpContext.Current);
}
}
I also have the following BaseViewPage classes which allow me to expose the current user to my views:
public abstract class BaseViewPage : WebViewPage
{
public virtual new CustomPrincipal User
{
get { return base.User as CustomPrincipal; }
}
}
public abstract class BaseViewPage<TModel> : WebViewPage<TModel>
{
public virtual new CustomPrincipal User
{
get { return base.User as CustomPrincipal; }
}
}
FWIW, this requires <pages pageBaseType="Giftster.Web.Views.BaseViewPage"> to be in my View's Web.config file.
Immediately, after the httpContext.User = customPrincipal; line runs in AuthenticationManager.SetAuthCookie(), the type of object which ContextHelper.GetHttpContextBase().User returns is a CustomPrincipal. If I refresh the page, however, and put a break point in BaseViewPage, base.User as CustomPrincipal (and ContextHelper.GetHttpContextBase().User as CustomPrincipal for that matter) equals null. base.User is not null, though: It is of type GenericPrincipal, so there is either a casting issue or a problem with storing/retrieving the correct type.
Why is base.User in my BaseViewPage not of type CustomPrincipal?
Thanks in advance.
You need to create your CustomPrincipal from the cookie in each request and add it to the current context. Add the following to the Global.asax.cs file
protected void Application_PostAuthenticateRequest(object sender, EventArgs e)
{
// Get the authentication cookie
HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
// If the cookie can't be found, don't issue the ticket
if (authCookie == null)
{
return;
}
// Extract the forms authentication cookie
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
// Deserialise user data
JavaScriptSerializer serializer = new JavaScriptSerializer();
CustomPrincipalSerializeModel data = serializer.Deserialize<CustomPrincipalSerializeModel>(authTicket.UserData);
// Create principal
CustomPrincipal principal = new CustomPrincipal(authTicket.Name);
// Set the properties of CustomPrincipal
principal.Email = data.Email;
.... // etc
// Assign to current context
HttpContext.Current.User = principal;
}
Note also the following line is not required in you SetAuthCookie() method
httpContext.User = customPrincipal;
You might also consider adding the following to a BaseController (from which all other controllers derive) so the CustomPrincipal properties can be accessed easily in each controller method
public new CustomPrincipalSerializeModel User
{
get { return (CustomPrincipal)HttpContext.User; }
}

How do I pass value to MVC3 master page ( _layout)?

I have a custom modelbinder, its check the authentication cookie and return the value.
public class UserDataModelBinder<T> : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (controllerContext.RequestContext.HttpContext.Request.IsAuthenticated)
{
var cookie =
controllerContext.RequestContext.HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
if (cookie == null)
return null;
var decrypted = FormsAuthentication.Decrypt(cookie.Value);
if (!string.IsNullOrWhiteSpace(decrypted.UserData))
return JsonSerializer.DeserializeFromString<T>(decrypted.UserData);
}
return null;
}
}
if I need to use it, I just need to pass it to the action. everything works.
public ActionResult Index(UserData userData)
{
AccountLoginWidgetVM model = new AccountLoginWidgetVM();
if (null != userData)
model.UserData = userData;
return View(userData);
}
However, I want to use it in my master page, because once user login, i want to display their info on the top on every page. I tried a few things, coudln't get it work
#Html.RenderPartial("LoginPartial", ???model here??)
We did it as follows:
Defined separate viewmodel for masterpages.
public class MasterPageViewModel
{
public Guid CurrentUserId { get; set; }
public string CurrentUserFullName { get; set; }
}
Added injection filter and filter provider.
public class MasterPageViewModelInjectorFilterProvider: IFilterProvider
{
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
return new [] {new Filter(new MasterPageViewModelInjectorFilter(), FilterScope.Action, null), };
}
private class MasterPageViewModelInjectorFilter: IResultFilter
{
public void OnResultExecuting(ResultExecutingContext filterContext)
{
var viewResult = filterContext.Result as ViewResult;
if (viewResult == null)
return;
if (viewResult.ViewBag.MasterPageViewModel != null)
return;
//setup model whichever way you want
var viewModel = new MasterPageViewModel();
//inject model into ViewBag
viewResult.ViewBag.MasterPageViewModel = viewModel;
}
public void OnResultExecuted(ResultExecutedContext filterContext)
{
}
}
}
Configure filter provider:
//in Application_Start
FilterProviders.Providers.Add(new MasterPageViewModelInjectorFilterProvider());
Use in master:
ViewBag.MasterPageViewModel
This way you have fine uncoupled architecture. Of course you can combine it with Dependency Injection (we do, but I left it out for clarity) and configure your action filter for every action whichever way you want.
In this case you can use ViewBag.
public ActionResult Index(UserData userData)
{
AccountLoginWidgetVM model = new AccountLoginWidgetVM();
if (null != userData)
model.UserData = userData;
ViewBag.UserData = userData;
return View(userData);
}
#Html.RenderPartial("LoginPartial", ViewBag.UserData)
You have to make sure that userData is not null. If it'll be null the passed model will be default model of the view.

Moq Roles.AddUserToRole test

I am writing unit tests for a project in ASP.NET MVC 1.0 using Moq and MvcContrib TestHelper classes. I have run into a problem.
When I come to Roles.AddUserToRole in my AccountController, I get a System.NotSupportedException. The Roles class is static and Moq cannot mock a static class.
What can I do?
You could use a pattern like DI (Dependency Injection). In your case, I would pass a RoleProvider to the AccountController, which would be the default RoleProvider by default, and a mock object in your tests. Something like:
public class AccountController
{
private MembershipProvider _provider;
private RoleProvider roleProvider;
public AccountController()
: this(null, null)
{
}
public AccountController(MembershipProvider provider, RoleProvider roleProvider)
{
_provider = provider ?? Membership.Provider;
this.roleProvider = roleProvider ?? System.Web.Security.Roles.Provider;
}
}
The MVC runtime will call the default constructor, which in turn will initialize the AccountController with the default role provider. In your unit test, you can directly call the overloaded constructor, and pass a MockRoleProvider (or use Moq to create it for you):
[Test]
public void AccountControllerTest()
{
AccountController controller = new AccountController(new MockMembershipProvider(), new MockRoleProvider());
}
EDIT: And here's how I mocked the entire HttpContext, including the principal user.
To get a Moq version of the HttpContext:
public static HttpContextBase GetHttpContext(IPrincipal principal)
{
var httpContext = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var response = new Mock<HttpResponseBase>();
var session = new Mock<HttpSessionStateBase>();
var server = new Mock<HttpServerUtilityBase>();
var user = principal;
httpContext.Setup(ctx => ctx.Request).Returns(request.Object);
httpContext.Setup(ctx => ctx.Response).Returns(response.Object);
httpContext.Setup(ctx => ctx.Session).Returns(session.Object);
httpContext.Setup(ctx => ctx.Server).Returns(server.Object);
httpContext.Setup(ctx => ctx.User).Returns(user);
return httpContext.Object;
}
A mock implementation of Principal:
public class MockPrincipal : IPrincipal
{
private IIdentity _identity;
private readonly string[] _roles;
public MockPrincipal(IIdentity identity, string[] roles)
{
_identity = identity;
_roles = roles;
}
public IIdentity Identity
{
get { return _identity; }
set { this._identity = value; }
}
public bool IsInRole(string role)
{
if (_roles == null)
return false;
return _roles.Contains(role);
}
}
A MockIdentity:
public class MockIdentity : IIdentity
{
private readonly string _name;
public MockIdentity(string userName) {
_name = userName;
}
public override string AuthenticationType
{
get { throw new System.NotImplementedException(); }
}
public override bool IsAuthenticated
{
get { return !String.IsNullOrEmpty(_name); }
}
public override string Name
{
get { return _name; }
}
}
And the magic call:
MockIdentity identity = new MockIdentity("JohnDoe");
var httpContext = MoqHelpers.GetHttpContext(new MockPrincipal(identity, null));
Note that I edited the code above to leave out some custom stuff, but I'm quite sure this should still work.
Now I run into another problem when I try to test the ChangePassword() method in ASP.NET MVC.
try
{
if (MembershipService.ChangePassword(User.Identity.Name, currentPassword, newPassword))
{
if (!TempData.ContainsKey("ChangePassword_success"))
{
TempData.Add("ChangePassword_success", true);
}
return PartialView("ChangePassword");
}
Now I get that User is null, when I reach this line. In my testclass I have:
mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);
I thought that this would work, but it doesn't care for that I send "johndoe". And If I were to mock IPrincipal, the User property is readonly.
TypeMock Isolator does mocking of statics etc. But I second (and +1'd) Razzie's answer.
I have done what you coded, but I still get that User is null when it reaches:
mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);
In my Testclass I have:
//Arrange (Set up a scenario)
var mockMembershipService = new Mock<IMembershipService>();
MockIdentity identity = new MockIdentity("JohnDoe");
var httpContext = MoqHelpers.GetHttpContext(new MockPrincipal(identity, null));
var controller = new AccountController(null, mockMembershipService.Object, null, null, null);
string currentPassword = "qwerty";
string newPassword = "123456";
string confirmPassword = "123456";
// Expectations
mockMembershipService.Setup(pw => pw.MinPasswordLength).Returns(6);
mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);
Do I call my cp.ChangePassword with wrong parameters? And should MVCContrib Testhelpers classes be able to mock Http context and so on? I just can't find info for how to setup User.Identity.Name with MVCContrib.
I have used this from a tutorial to test something (mock) session:
var builder = new TestControllerBuilder();
var controller = new AccountController(mockFormsAuthentication.Object, mockMembershipService.Object, mockUserRepository.Object, null, mockBandRepository.Object);
builder.InitializeController(controller);
EDIT: I have come a little further:
MockIdentity identity = new MockIdentity("JohnDoe");
var httpContext = MoqHelpers.GetHttpContext(new MockPrincipal(identity, null));
var controller = new AccountController(null, mockMembershipService.Object, null, null, null);
controller.ControllerContext = new ControllerContext(httpContext, new RouteData(), controller);
but my now I can't get my cp.ChangePassword in the expectation to return true:
mockMembershipService.Setup(cp => cp.ChangePassword("johndoe", currentPassword, newPassword)).Returns(true);
I am sending "johndoe" string, because, it requires a string as a parameter for User.Identity.Name, but it doesn't return true.

ASP.NET MVC - How to access Session data in places other than Controller and Views

We can access session data in controllers and views like this:
Session["SessionKey1"]
How do you access Session values from a class other than a controller or view?
I'd use dependency injection and pass the instance of the HttpContext (or just the session) to the class that needs access to the Session. The other alternative is to reference HttpContext.Current, but that will make it harder to test since it's a static object.
public ActionResult MyAction()
{
var foo = new Foo( this.HttpContext );
...
}
public class Foo
{
private HttpContextBase Context { get; set; }
public Foo( HttpContextBase context )
{
this.Context = context;
}
public void Bar()
{
var value = this.Context.Session["barKey"];
...
}
}
You just need to call it through the HttpContext like so:
HttpContext.Current.Session["MyValue"] = "Something";
Here is my version of a solution for this problem. Notice that I also use a dependency injection as well, the only major difference is that the "session" object is accessed through a Singleton
private iSession _Session;
private iSession InternalSession
{
get
{
if (_Session == null)
{
_Session = new SessionDecorator(this.Session);
}
return _Session;
}
}
Here is the SessionDecorator class, which uses a Decorator pattern to wrap the session around an interface :
public class SessionDecorator : iSession
{
private HttpSessionStateBase _Session;
private const string SESSIONKEY1= "SESSIONKEY1";
private const string SESSIONKEY2= "SESSIONKEY2";
public SessionDecorator(HttpSessionStateBase session)
{
_Session = session;
}
int iSession.AValue
{
get
{
return _Session[SESSIONKEY1] == null ? 1 : Convert.ToInt32(_Session[SESSIONKEY1]);
}
set
{
_Session[SESSIONKEY1] = value;
}
}
int iSession.AnotherValue
{
get
{
return _Session[SESSIONKEY2] == null ? 0 : Convert.ToInt32(_Session[SESSIONKEY2]);
}
set
{
_Session[SESSIONKEY2] = value;
}
}
}`
Hope this helps :)
Haven't done it myself, but this sample from Chad Meyer's blog might help (from this post: http://www.chadmyers.com/Blog/archive/2007/11/30/asp.net-webforms-and-mvc-in-the-same-project.aspx)
[ControllerAction]
public void Edit(int id)
{
IHttpSessionState session = HttpContext.Session;
if (session["LoggedIn"] == null || ((bool)session["LoggedIn"] != true))
RenderView("NotLoggedIn");
Product p = SomeFancyDataAccess.GetProductByID(id);
RenderView("Edit", p);
}
I would also wrap all session variables into a single class file. That way you can use intelliSense to select them. This cuts down on the number of paces in code where you need to specify the "strings" for the session.

Resources