Implement read-only HeaderDictionary (#958)
This commit is contained in:
parent
f287c46bad
commit
3e3772eecd
|
|
@ -58,13 +58,13 @@ namespace Microsoft.AspNetCore.Http
|
||||||
}
|
}
|
||||||
return StringValues.Empty;
|
return StringValues.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (key == null)
|
if (key == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(key));
|
throw new ArgumentNullException(nameof(key));
|
||||||
}
|
}
|
||||||
|
ThrowIfReadOnly();
|
||||||
|
|
||||||
if (StringValues.IsNullOrEmpty(value))
|
if (StringValues.IsNullOrEmpty(value))
|
||||||
{
|
{
|
||||||
|
|
@ -90,7 +90,11 @@ namespace Microsoft.AspNetCore.Http
|
||||||
StringValues IDictionary<string, StringValues>.this[string key]
|
StringValues IDictionary<string, StringValues>.this[string key]
|
||||||
{
|
{
|
||||||
get { return Store[key]; }
|
get { return Store[key]; }
|
||||||
set { this[key] = value; }
|
set
|
||||||
|
{
|
||||||
|
ThrowIfReadOnly();
|
||||||
|
this[key] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long? ContentLength
|
public long? ContentLength
|
||||||
|
|
@ -110,6 +114,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
ThrowIfReadOnly();
|
||||||
if (value.HasValue)
|
if (value.HasValue)
|
||||||
{
|
{
|
||||||
this[HeaderNames.ContentLength] = HeaderUtilities.FormatNonNegativeInt64(value.Value);
|
this[HeaderNames.ContentLength] = HeaderUtilities.FormatNonNegativeInt64(value.Value);
|
||||||
|
|
@ -125,25 +130,13 @@ namespace Microsoft.AspNetCore.Http
|
||||||
/// Gets the number of elements contained in the <see cref="HeaderDictionary" />;.
|
/// Gets the number of elements contained in the <see cref="HeaderDictionary" />;.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The number of elements contained in the <see cref="HeaderDictionary" />.</returns>
|
/// <returns>The number of elements contained in the <see cref="HeaderDictionary" />.</returns>
|
||||||
public int Count
|
public int Count => Store?.Count ?? 0;
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Store?.Count ?? 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value that indicates whether the <see cref="HeaderDictionary" /> is in read-only mode.
|
/// Gets a value that indicates whether the <see cref="HeaderDictionary" /> is in read-only mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>true if the <see cref="HeaderDictionary" /> is in read-only mode; otherwise, false.</returns>
|
/// <returns>true if the <see cref="HeaderDictionary" /> is in read-only mode; otherwise, false.</returns>
|
||||||
public bool IsReadOnly
|
public bool IsReadOnly { get; set; }
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ICollection<string> Keys
|
public ICollection<string> Keys
|
||||||
{
|
{
|
||||||
|
|
@ -179,6 +172,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("The key is null");
|
throw new ArgumentNullException("The key is null");
|
||||||
}
|
}
|
||||||
|
ThrowIfReadOnly();
|
||||||
if (Store == null)
|
if (Store == null)
|
||||||
{
|
{
|
||||||
Store = new Dictionary<string, StringValues>(1, StringComparer.OrdinalIgnoreCase);
|
Store = new Dictionary<string, StringValues>(1, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
@ -197,6 +191,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(key));
|
throw new ArgumentNullException(nameof(key));
|
||||||
}
|
}
|
||||||
|
ThrowIfReadOnly();
|
||||||
|
|
||||||
if (Store == null)
|
if (Store == null)
|
||||||
{
|
{
|
||||||
|
|
@ -210,6 +205,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
|
ThrowIfReadOnly();
|
||||||
Store?.Clear();
|
Store?.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,6 +266,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
/// <returns>true if the specified object was removed from the collection; otherwise, false.</returns>
|
/// <returns>true if the specified object was removed from the collection; otherwise, false.</returns>
|
||||||
public bool Remove(KeyValuePair<string, StringValues> item)
|
public bool Remove(KeyValuePair<string, StringValues> item)
|
||||||
{
|
{
|
||||||
|
ThrowIfReadOnly();
|
||||||
if (Store == null)
|
if (Store == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -291,6 +288,7 @@ namespace Microsoft.AspNetCore.Http
|
||||||
/// <returns>true if the specified object was removed from the collection; otherwise, false.</returns>
|
/// <returns>true if the specified object was removed from the collection; otherwise, false.</returns>
|
||||||
public bool Remove(string key)
|
public bool Remove(string key)
|
||||||
{
|
{
|
||||||
|
ThrowIfReadOnly();
|
||||||
if (Store == null)
|
if (Store == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -356,6 +354,14 @@ namespace Microsoft.AspNetCore.Http
|
||||||
return Store.GetEnumerator();
|
return Store.GetEnumerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ThrowIfReadOnly()
|
||||||
|
{
|
||||||
|
if (IsReadOnly)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("The response headers cannot be modified because the response has already started.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>
|
public struct Enumerator : IEnumerator<KeyValuePair<string, StringValues>>
|
||||||
{
|
{
|
||||||
// Do NOT make this readonly, or MoveNext will not work
|
// Do NOT make this readonly, or MoveNext will not work
|
||||||
|
|
|
||||||
|
|
@ -68,5 +68,40 @@ namespace Microsoft.AspNetCore.Http
|
||||||
var result = headers.GetCommaSeparatedValues("Header1");
|
var result = headers.GetCommaSeparatedValues("Header1");
|
||||||
Assert.Equal(new[] { "Value1", "Value2" }, result);
|
Assert.Equal(new[] { "Value1", "Value2" }, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ReadActionsWorkWhenReadOnly()
|
||||||
|
{
|
||||||
|
var headers = new HeaderDictionary(
|
||||||
|
new Dictionary<string, StringValues>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ "Header1", "Value1" }
|
||||||
|
});
|
||||||
|
|
||||||
|
headers.IsReadOnly = true;
|
||||||
|
|
||||||
|
Assert.Single(headers);
|
||||||
|
Assert.Equal<string>(new[] { "Header1" }, headers.Keys);
|
||||||
|
Assert.True(headers.ContainsKey("header1"));
|
||||||
|
Assert.False(headers.ContainsKey("header2"));
|
||||||
|
Assert.Equal("Value1", headers["header1"]);
|
||||||
|
Assert.Equal(new[] { "Value1" }, headers["header1"].ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void WriteActionsThrowWhenReadOnly()
|
||||||
|
{
|
||||||
|
var headers = new HeaderDictionary();
|
||||||
|
headers.IsReadOnly = true;
|
||||||
|
|
||||||
|
Assert.Throws<InvalidOperationException>(() => headers["header1"] = "value1");
|
||||||
|
Assert.Throws<InvalidOperationException>(() => ((IDictionary<string, StringValues>)headers)["header1"] = "value1");
|
||||||
|
Assert.Throws<InvalidOperationException>(() => headers.ContentLength = 12);
|
||||||
|
Assert.Throws<InvalidOperationException>(() => headers.Add(new KeyValuePair<string, StringValues>("header1", "value1")));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => headers.Add("header1", "value1"));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => headers.Clear());
|
||||||
|
Assert.Throws<InvalidOperationException>(() => headers.Remove(new KeyValuePair<string, StringValues>("header1", "value1")));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => headers.Remove("header1"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue