[Fixes #3068] TempData fails silently without sessions middleware
This commit is contained in:
parent
7b58e03f90
commit
130d94eb7e
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue