Session API review changes.

This commit is contained in:
Kiran Challa 2015-05-27 15:17:38 -07:00
parent ed339a35d2
commit 6407a1672d
14 changed files with 150 additions and 139 deletions

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Security.Claims;
using System.Threading;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.AspNet.Http.Features;
namespace Microsoft.AspNet.Http
{
@ -31,7 +32,7 @@ namespace Microsoft.AspNet.Http
public abstract CancellationToken RequestAborted { get; set; }
public abstract ISessionCollection Session { get; }
public abstract ISession Session { get; set; }
public abstract void Abort();

View File

@ -1,21 +0,0 @@
// 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.
using System;
using System.Collections.Generic;
namespace Microsoft.AspNet.Http
{
public interface ISessionCollection : IEnumerable<KeyValuePair<string, byte[]>>
{
byte[] this[string key] { get; set; }
bool TryGetValue(string key, out byte[] value);
void Set(string key, ArraySegment<byte> value);
void Remove(string key);
void Clear();
}
}

View File

@ -2,6 +2,7 @@
"version": "1.0.0-*",
"description": "ASP.NET 5 HTTP object model. HttpContext and family.",
"dependencies": {
"Microsoft.AspNet.Http.Features": "1.0.0-*",
"Microsoft.Framework.ActivatorUtilities.Sources": { "type": "build", "version": "1.0.0-*" },
"Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" },
"Microsoft.Framework.WebEncoders.Core": "1.0.0-*"

View File

@ -1,14 +1,14 @@
// 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.
using System;
using System.Text;
using Microsoft.AspNet.Http.Features;
namespace Microsoft.AspNet.Http
{
public static class SessionCollectionExtensions
public static class SessionExtensions
{
public static void SetInt32(this ISessionCollection session, string key, int value)
public static void SetInt32(this ISession session, string key, int value)
{
var bytes = new byte[]
{
@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Http
session.Set(key, bytes);
}
public static int? GetInt32(this ISessionCollection session, string key)
public static int? GetInt32(this ISession session, string key)
{
var data = session.Get(key);
if (data == null || data.Length < 4)
@ -30,12 +30,12 @@ namespace Microsoft.AspNet.Http
return data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
}
public static void SetString(this ISessionCollection session, string key, string value)
public static void SetString(this ISession session, string key, string value)
{
session.Set(key, Encoding.UTF8.GetBytes(value));
}
public static string GetString(this ISessionCollection session, string key)
public static string GetString(this ISession session, string key)
{
var data = session.Get(key);
if (data == null)
@ -45,16 +45,11 @@ namespace Microsoft.AspNet.Http
return Encoding.UTF8.GetString(data);
}
public static byte[] Get(this ISessionCollection session, string key)
public static byte[] Get(this ISession session, string key)
{
byte[] value = null;
session.TryGetValue(key, out value);
return value;
}
public static void Set(this ISessionCollection session, string key, byte[] value)
{
session.Set(key, new ArraySegment<byte>(value));
}
}
}

View File

@ -3,7 +3,6 @@
"description": "ASP.NET 5 common extension methods for HTTP abstractions and IApplicationBuilder.",
"dependencies": {
"Microsoft.AspNet.Http.Abstractions": "1.0.0-*",
"Microsoft.AspNet.Http.Features": "1.0.0-*",
"Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" },
"Microsoft.Framework.WebEncoders.Core": "1.0.0-*",
"Microsoft.Net.Http.Headers": "1.0.0-*"

View File

@ -1,20 +1,20 @@
// 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.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Http.Features
{
public interface ISession
{
void Load();
Task LoadAsync();
void Commit();
Task CommitAsync();
bool TryGetValue(string key, out byte[] value);
void Set(string key, ArraySegment<byte> value);
void Set(string key, byte[] value);
void Remove(string key);

View File

@ -1,12 +0,0 @@
// 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.
namespace Microsoft.AspNet.Http.Features
{
public interface ISessionFactory
{
bool IsAvailable { get; }
ISession Create();
}
}

View File

@ -3,11 +3,8 @@
namespace Microsoft.AspNet.Http.Features
{
// TODO: Is there any reason not to flatten the Factory down into the Feature?
public interface ISessionFeature
{
ISessionFactory Factory { get; set; }
ISession Session { get; set; }
}
}

View File

@ -1,4 +1,4 @@
// 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.
namespace Microsoft.AspNet.Http.Features

View File

@ -124,24 +124,27 @@ namespace Microsoft.AspNet.Http.Internal
set { LifetimeFeature.RequestAborted = value; }
}
public override ISessionCollection Session
public override ISession Session
{
get
{
var feature = SessionFeature;
if (feature == null)
{
throw new InvalidOperationException("Session has not been configured for this application or request.");
throw new InvalidOperationException("Session has not been configured for this application " +
"or request.");
}
if (feature.Session == null)
return feature.Session;
}
set
{
var feature = SessionFeature;
if (feature == null)
{
if (feature.Factory == null)
{
throw new InvalidOperationException("No ISessionFactory available to create the ISession.");
}
feature.Session = feature.Factory.Create();
feature = new DefaultSessionFeature();
_session.Update(_features, feature);
}
return new SessionCollection(feature.Session);
feature.Session = value;
}
}

View File

@ -0,0 +1,14 @@
// 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.
namespace Microsoft.AspNet.Http.Features.Internal
{
/// <summary>
/// This type exists only for the purpose of unit testing where the user can directly set the
/// <see cref="HttpContext.Session"/> property without the need for creating a <see cref="ISessionFeature"/>.
/// </summary>
public class DefaultSessionFeature : ISessionFeature
{
public ISession Session { get; set; }
}
}

View File

@ -1,74 +0,0 @@
// 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.
using System;
using System.Collections;
using System.Collections.Generic;
using Microsoft.AspNet.Http.Features;
namespace Microsoft.AspNet.Http.Internal
{
public class SessionCollection : ISessionCollection
{
private readonly ISession _session;
public SessionCollection(ISession session)
{
_session = session;
}
public byte[] this[string key]
{
get
{
byte[] value;
TryGetValue(key, out value);
return value;
}
set
{
if (value == null)
{
Remove(key);
}
else
{
Set(key, new ArraySegment<byte>(value));
}
}
}
public bool TryGetValue(string key, out byte[] value)
{
return _session.TryGetValue(key, out value);
}
public void Set(string key, ArraySegment<byte> value)
{
_session.Set(key, value);
}
public void Remove(string key)
{
_session.Remove(key);
}
public void Clear()
{
_session.Clear();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<KeyValuePair<string, byte[]>> GetEnumerator()
{
foreach (var key in _session.Keys)
{
yield return new KeyValuePair<string, byte[]>(key, this[key]);
}
}
}
}

View File

@ -4,7 +4,6 @@
"dependencies": {
"Microsoft.AspNet.FeatureModel": "1.0.0-*",
"Microsoft.AspNet.Http.Abstractions": "1.0.0-*",
"Microsoft.AspNet.Http.Features": "1.0.0-*",
"Microsoft.AspNet.WebUtilities": "1.0.0-*",
"Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" },
"Microsoft.Net.Http.Headers": "1.0.0-*"

View File

@ -1,10 +1,13 @@
// 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.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNet.FeatureModel;
using Microsoft.AspNet.Http.Features;
using Microsoft.AspNet.Http.Features.Internal;
using Xunit;
@ -12,6 +15,69 @@ namespace Microsoft.AspNet.Http.Internal
{
public class DefaultHttpContextTests
{
[Fact]
public void GetOnSessionProperty_ThrowsOnMissingSessionFeature()
{
// Arrange
var context = new DefaultHttpContext();
// Act & Assert
var exception = Assert.Throws<InvalidOperationException>(() => context.Session);
Assert.Equal("Session has not been configured for this application or request.", exception.Message);
}
[Fact]
public void GetOnSessionProperty_ReturnsAvailableSession()
{
// Arrange
var context = new DefaultHttpContext();
var session = new TestSession();
session.Set("key1", null);
session.Set("key2", null);
var feature = new BlahSessionFeature();
feature.Session = session;
context.SetFeature<ISessionFeature>(feature);
// Act & Assert
Assert.Same(session, context.Session);
context.Session.Set("key3", null);
Assert.Equal(3, context.Session.Keys.Count());
}
[Fact]
public void AllowsSettingSession_WithoutSettingUpSessionFeature_Upfront()
{
// Arrange
var session = new TestSession();
var context = new DefaultHttpContext();
// Act
context.Session = session;
// Assert
Assert.Same(session, context.Session);
}
[Fact]
public void SettingSession_OverridesAvailableSession()
{
// Arrange
var context = new DefaultHttpContext();
var session = new TestSession();
session.Set("key1", null);
session.Set("key2", null);
var feature = new BlahSessionFeature();
feature.Session = session;
context.SetFeature<ISessionFeature>(feature);
// Act
context.Session = new TestSession();
// Assert
Assert.NotSame(session, context.Session);
Assert.Empty(context.Session.Keys);
}
[Fact]
public void EmptyUserIsNeverNull()
{
@ -73,5 +139,48 @@ namespace Microsoft.AspNet.Http.Internal
var context = new DefaultHttpContext();
return context;
}
private class TestSession : ISession
{
private Dictionary<string, byte[]> _store
= new Dictionary<string, byte[]>(StringComparer.OrdinalIgnoreCase);
public IEnumerable<string> Keys { get { return _store.Keys; } }
public void Clear()
{
_store.Clear();
}
public Task CommitAsync()
{
return Task.FromResult(0);
}
public Task LoadAsync()
{
return Task.FromResult(0);
}
public void Remove(string key)
{
_store.Remove(key);
}
public void Set(string key, byte[] value)
{
_store[key] = value;
}
public bool TryGetValue(string key, out byte[] value)
{
return _store.TryGetValue(key, out value);
}
}
private class BlahSessionFeature : ISessionFeature
{
public ISession Session { get; set; }
}
}
}