[Fixes #3068] TempData fails silently without sessions middleware

This commit is contained in:
Kiran Challa 2015-09-28 14:47:56 -07:00
parent 7b58e03f90
commit 130d94eb7e
4 changed files with 86 additions and 95 deletions

View File

@ -59,17 +59,8 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
if (!IsSessionEnabled(context)) // Accessing Session property will throw if the session middleware is not enabled.
{
// Session middleware is not enabled. No-op
return null;
}
var session = context.Session; var session = context.Session;
if (session == null)
{
return null;
}
var tempDataDictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase); var tempDataDictionary = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
byte[] value; byte[] value;
@ -162,6 +153,9 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
throw new ArgumentNullException(nameof(context)); throw new ArgumentNullException(nameof(context));
} }
// Accessing Session property will throw if the session middleware is not enabled.
var session = context.Session;
var hasValues = (values != null && values.Count > 0); var hasValues = (values != null && values.Count > 0);
if (hasValues) if (hasValues)
{ {
@ -171,9 +165,6 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
EnsureObjectCanBeSerialized(item); EnsureObjectCanBeSerialized(item);
} }
// Accessing Session property will throw if the session middleware is not enabled.
var session = context.Session;
using (var memoryStream = new MemoryStream()) using (var memoryStream = new MemoryStream())
{ {
using (var writer = new BsonWriter(memoryStream)) using (var writer = new BsonWriter(memoryStream))
@ -183,18 +174,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
} }
} }
} }
else if (IsSessionEnabled(context)) else
{ {
var session = context.Session;
session.Remove(TempDataSessionStateKey); session.Remove(TempDataSessionStateKey);
} }
} }
private static bool IsSessionEnabled(HttpContext context)
{
return context.Features.Get<ISessionFeature>() != null;
}
internal void EnsureObjectCanBeSerialized(object item) internal void EnsureObjectCanBeSerialized(object item)
{ {
var itemType = item.GetType(); var itemType = item.GetType();

View File

@ -101,7 +101,13 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
/// <inheritdoc /> /// <inheritdoc />
public void Keep() public void Keep()
{ {
Load(); // if the data is not loaded, we can assume none of it has been read
// and so silently return.
if (!_loaded)
{
return;
}
_retainedKeys.Clear(); _retainedKeys.Clear();
_retainedKeys.UnionWith(_data.Keys); _retainedKeys.UnionWith(_data.Keys);
} }

View File

@ -16,56 +16,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
public class SessionStateTempDataProviderTest public class SessionStateTempDataProviderTest
{ {
[Fact] [Fact]
public void Load_NullSession_ReturnsEmptyDictionary() public void Load_ThrowsException_WhenSessionIsNotEnabled()
{
// Arrange
var testProvider = new SessionStateTempDataProvider();
// Act
var tempDataDictionary = testProvider.LoadTempData(
GetHttpContext(session: null, sessionEnabled: true));
// Assert
Assert.Null(tempDataDictionary);
}
[Fact]
public void Load_NonNullSession_NoSessionData_ReturnsEmptyDictionary()
{
// Arrange
var testProvider = new SessionStateTempDataProvider();
// Act
var tempDataDictionary = testProvider.LoadTempData(
GetHttpContext(Mock.Of<ISession>()));
// Assert
Assert.Empty(tempDataDictionary);
}
[Fact]
public void Save_NullSession_NullDictionary_DoesNotThrow()
{
// Arrange
var testProvider = new SessionStateTempDataProvider();
// Act & Assert (does not throw)
testProvider.SaveTempData(GetHttpContext(session: null, sessionEnabled: false), null);
}
[Fact]
public void Save_NullSession_EmptyDictionary_DoesNotThrow()
{
// Arrange
var testProvider = new SessionStateTempDataProvider();
// Act & Assert (does not throw)
testProvider.SaveTempData(
GetHttpContext(session: null, sessionEnabled: false), new Dictionary<string, object>());
}
[Fact]
public void Save_NullSession_NonEmptyDictionary_Throws()
{ {
// Arrange // Arrange
var testProvider = new SessionStateTempDataProvider(); var testProvider = new SessionStateTempDataProvider();
@ -73,13 +24,38 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
// Act & Assert // Act & Assert
Assert.Throws<InvalidOperationException>(() => Assert.Throws<InvalidOperationException>(() =>
{ {
testProvider.SaveTempData( testProvider.LoadTempData(GetHttpContext(sessionEnabled: false));
GetHttpContext(session: null, sessionEnabled: false),
new Dictionary<string, object> { { "foo", "bar" } }
);
}); });
} }
[Fact]
public void Save_ThrowsException_WhenSessionIsNotEnabled()
{
// Arrange
var testProvider = new SessionStateTempDataProvider();
var values = new Dictionary<string, object>();
values.Add("key1", "value1");
// Act & Assert
Assert.Throws<InvalidOperationException>(() =>
{
testProvider.SaveTempData(GetHttpContext(sessionEnabled: false), values);
});
}
[Fact]
public void Load_ReturnsEmptyDictionary_WhenNoSessionDataIsAvailable()
{
// Arrange
var testProvider = new SessionStateTempDataProvider();
// Act
var tempDataDictionary = testProvider.LoadTempData(GetHttpContext());
// Assert
Assert.Empty(tempDataDictionary);
}
public static TheoryData<object, Type> InvalidTypes public static TheoryData<object, Type> InvalidTypes
{ {
get get
@ -97,7 +73,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
[Theory] [Theory]
[MemberData(nameof(InvalidTypes))] [MemberData(nameof(InvalidTypes))]
public void EnsureObjectCanBeSerialized_InvalidType_Throws(object value, Type type) public void EnsureObjectCanBeSerialized_ThrowsException_OnInvalidType(object value, Type type)
{ {
// Arrange // Arrange
var testProvider = new SessionStateTempDataProvider(); var testProvider = new SessionStateTempDataProvider();
@ -107,7 +83,8 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
testProvider.EnsureObjectCanBeSerialized(value); testProvider.EnsureObjectCanBeSerialized(value);
}); });
Assert.Equal($"The '{typeof(SessionStateTempDataProvider).FullName}' cannot serialize an object of type '{type}' to session state.", Assert.Equal($"The '{typeof(SessionStateTempDataProvider).FullName}' cannot serialize " +
$"an object of type '{type}' to session state.",
exception.Message); exception.Message);
} }
@ -127,7 +104,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
[Theory] [Theory]
[MemberData(nameof(InvalidDictionaryTypes))] [MemberData(nameof(InvalidDictionaryTypes))]
public void EnsureObjectCanBeSerialized_InvalidDictionaryType_Throws(object value, Type type) public void EnsureObjectCanBeSerialized_ThrowsException_OnInvalidDictionaryType(object value, Type type)
{ {
// Arrange // Arrange
var testProvider = new SessionStateTempDataProvider(); var testProvider = new SessionStateTempDataProvider();
@ -137,7 +114,8 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
testProvider.EnsureObjectCanBeSerialized(value); testProvider.EnsureObjectCanBeSerialized(value);
}); });
Assert.Equal($"The '{typeof(SessionStateTempDataProvider).FullName}' cannot serialize a dictionary with a key of type '{type}' to session state.", Assert.Equal($"The '{typeof(SessionStateTempDataProvider).FullName}' cannot serialize a dictionary " +
$"with a key of type '{type}' to session state.",
exception.Message); exception.Message);
} }
@ -163,7 +141,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
[Theory] [Theory]
[MemberData(nameof(ValidTypes))] [MemberData(nameof(ValidTypes))]
public void EnsureObjectCanBeSerialized_ValidType_DoesNotThrow(object value) public void EnsureObjectCanBeSerialized_DoesNotThrow_OnValidType(object value)
{ {
// Arrange // Arrange
var testProvider = new SessionStateTempDataProvider(); var testProvider = new SessionStateTempDataProvider();
@ -181,7 +159,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
{ "string", "value" } { "string", "value" }
}; };
var context = GetHttpContext(new TestSession(), true); var context = GetHttpContext();
// Act // Act
testProvider.SaveTempData(context, input); testProvider.SaveTempData(context, input);
@ -201,7 +179,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
{ "int", 10 } { "int", 10 }
}; };
var context = GetHttpContext(new TestSession(), true); var context = GetHttpContext();
// Act // Act
testProvider.SaveTempData(context, input); testProvider.SaveTempData(context, input);
@ -223,7 +201,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
{ "bool", value } { "bool", value }
}; };
var context = GetHttpContext(new TestSession(), true); var context = GetHttpContext();
// Act // Act
testProvider.SaveTempData(context, input); testProvider.SaveTempData(context, input);
@ -244,7 +222,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
{ "DateTime", inputDatetime } { "DateTime", inputDatetime }
}; };
var context = GetHttpContext(new TestSession(), true); var context = GetHttpContext();
// Act // Act
testProvider.SaveTempData(context, input); testProvider.SaveTempData(context, input);
@ -265,7 +243,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
{ "Guid", inputGuid } { "Guid", inputGuid }
}; };
var context = GetHttpContext(new TestSession(), true); var context = GetHttpContext();
// Act // Act
testProvider.SaveTempData(context, input); testProvider.SaveTempData(context, input);
@ -285,7 +263,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
{ "List`string", new List<string> { "one", "two" } } { "List`string", new List<string> { "one", "two" } }
}; };
var context = GetHttpContext(new TestSession(), true); var context = GetHttpContext();
// Act // Act
testProvider.SaveTempData(context, input); testProvider.SaveTempData(context, input);
@ -311,7 +289,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
{ "Dictionary", inputDictionary } { "Dictionary", inputDictionary }
}; };
var context = GetHttpContext(new TestSession(), true); var context = GetHttpContext();
// Act // Act
testProvider.SaveTempData(context, input); testProvider.SaveTempData(context, input);
@ -331,7 +309,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
{ "EmptyDictionary", new Dictionary<string, int>() } { "EmptyDictionary", new Dictionary<string, int>() }
}; };
var context = GetHttpContext(new TestSession(), true); var context = GetHttpContext();
// Act // Act
testProvider.SaveTempData(context, input); testProvider.SaveTempData(context, input);
@ -347,12 +325,12 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
public int DummyInt { get; set; } public int DummyInt { get; set; }
} }
private HttpContext GetHttpContext(ISession session, bool sessionEnabled = true) private HttpContext GetHttpContext(bool sessionEnabled = true)
{ {
var httpContext = new DefaultHttpContext(); var httpContext = new DefaultHttpContext();
if(sessionEnabled) if (sessionEnabled)
{ {
httpContext.Features.Set<ISessionFeature>(new SessionFeature() { Session = session }); httpContext.Features.Set<ISessionFeature>(new SessionFeature() { Session = new TestSession() });
} }
return httpContext; return httpContext;
} }

View File

@ -1,8 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved. // Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Microsoft.AspNet.Http; using Microsoft.AspNet.Http;
using Microsoft.AspNet.Http.Internal;
using Moq; using Moq;
using Xunit; using Xunit;
@ -10,6 +12,29 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
{ {
public class TempDataDictionaryTest public class TempDataDictionaryTest
{ {
[Fact]
public void ThrowscdException_OnSettingValue_AndWhenSessionIsNotEnabled()
{
// Arrange
var tempData = new TempDataDictionary(GetHttpContextAccessor(), new SessionStateTempDataProvider());
// Act & Assert
Assert.Throws<InvalidOperationException>(() =>
{
tempData["key1"] = "value1";
});
}
[Fact]
public void Keep_DoesNotThrowException_WhenDataIsNotLoaded()
{
// Arrange
var tempData = new TempDataDictionary(GetHttpContextAccessor(), new SessionStateTempDataProvider());
// Act & Assert
tempData.Keep();
}
[Fact] [Fact]
public void TempData_Load_CreatesEmptyDictionaryIfProviderReturnsNull() public void TempData_Load_CreatesEmptyDictionaryIfProviderReturnsNull()
{ {
@ -214,10 +239,7 @@ namespace Microsoft.AspNet.Mvc.ViewFeatures
private static IHttpContextAccessor GetHttpContextAccessor() private static IHttpContextAccessor GetHttpContextAccessor()
{ {
var httpContext = new Mock<HttpContext>(); return new HttpContextAccessor() { HttpContext = new DefaultHttpContext() };
var httpContextAccessor = new Mock<IHttpContextAccessor>();
httpContextAccessor.Setup(h => h.HttpContext).Returns(httpContext.Object);
return httpContextAccessor.Object;
} }
} }
} }