-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathQueryCollection.cs
More file actions
116 lines (95 loc) · 3.09 KB
/
Copy pathQueryCollection.cs
File metadata and controls
116 lines (95 loc) · 3.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
using System.Collections;
using Microsoft.Extensions.Primitives;
namespace Shiny.Net.HttpServer;
/// <summary>
/// Parsed query string values. Lazily built: the raw query string is kept as-is until
/// something actually reads a value, so routes that ignore the query never pay for parsing.
/// </summary>
public sealed class QueryCollection : IEnumerable<KeyValuePair<string, StringValues>>
{
readonly Dictionary<string, StringValues> store = new(StringComparer.OrdinalIgnoreCase);
string? raw;
bool parsed;
/// <summary>The raw query string, without the leading '?'. Null when there was none.</summary>
public string? RawValue => this.raw;
public StringValues this[string key]
{
get
{
this.EnsureParsed();
return this.store.TryGetValue(key, out var value) ? value : StringValues.Empty;
}
}
public int Count
{
get
{
this.EnsureParsed();
return this.store.Count;
}
}
public ICollection<string> Keys
{
get
{
this.EnsureParsed();
return this.store.Keys;
}
}
public bool ContainsKey(string key)
{
this.EnsureParsed();
return this.store.ContainsKey(key);
}
public bool TryGetValue(string key, out StringValues value)
{
this.EnsureParsed();
return this.store.TryGetValue(key, out value);
}
/// <summary>Gets the first value for a key, or null when absent.</summary>
public string? GetFirst(string key)
{
this.EnsureParsed();
return this.store.TryGetValue(key, out var value) ? value.ToString() : null;
}
public IEnumerator<KeyValuePair<string, StringValues>> GetEnumerator()
{
this.EnsureParsed();
return this.store.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
internal void SetRaw(string? queryString)
{
this.raw = queryString;
this.parsed = false;
this.store.Clear();
}
internal void Reset() => this.SetRaw(null);
void EnsureParsed()
{
if (this.parsed)
return;
this.parsed = true;
if (string.IsNullOrEmpty(this.raw))
return;
var remaining = this.raw.AsSpan();
while (!remaining.IsEmpty)
{
var pairEnd = remaining.IndexOf('&');
var pair = pairEnd < 0 ? remaining : remaining[..pairEnd];
remaining = pairEnd < 0 ? default : remaining[(pairEnd + 1)..];
if (pair.IsEmpty)
continue;
var eq = pair.IndexOf('=');
var name = eq < 0 ? pair : pair[..eq];
var value = eq < 0 ? default : pair[(eq + 1)..];
if (name.IsEmpty)
continue;
var key = UrlDecoder.DecodeFormComponent(name);
var decoded = UrlDecoder.DecodeFormComponent(value);
this.store[key] = this.store.TryGetValue(key, out var existing)
? StringValues.Concat(existing, decoded)
: new StringValues(decoded);
}
}
}