Merge pull request #2333 from DuendeSoftware/jmdc/7.4/querystring-regression

Fixed a regression where the + character was not treated as a space in query params
This commit is contained in:
Adam Ralph 2026-01-22 11:05:20 +01:00 committed by GitHub
commit a9890cb9d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 38 additions and 4 deletions

View file

@ -260,15 +260,19 @@ internal static class StringExtensions
}
var kvp = pair.Split('=', 2);
var key = Uri.UnescapeDataString(kvp[0]);
var key = kvp[0];
var value = kvp.Length > 1 ? kvp[1] : null;
var unescaped = value.IsPresent() ? Uri.UnescapeDataString(kvp[1]) : null;
collection.Add(key, unescaped);
if (value.IsPresent())
{
collection.Add(Decode(key), Decode(value));
}
}
return collection;
}
private static string Decode(string value) => Uri.UnescapeDataString(value.Replace('+', ' '));
private static string? QueryString(this string url)
{
var queryStringStart = url.IndexOf('?', StringComparison.InvariantCulture);

View file

@ -3,10 +3,11 @@
using Duende.IdentityServer.Extensions;
using Microsoft.AspNetCore.WebUtilities;
namespace UnitTests.Extensions;
public class StringExtensionsTests
public class StringExtensionsTests()
{
private const string Category = "StringExtensions Tests";
@ -230,6 +231,35 @@ public class StringExtensionsTests
nvc["baz"].ShouldBe("qux+test");
}
private IEnumerable<string> AllCharactersAndEncodings()
{
for (var i = 0; i < 256; i++)
{
var c = Convert.ToChar(i);
var s = Convert.ToString(c);
yield return s;
yield return Uri.EscapeDataString(s);
}
}
[Fact]
public void ReadQueryStringAsNameValueCollection_should_decode_urlencoded_values_identically_to_QueryHelpersParseNullableQuery()
{
foreach (var c in AllCharactersAndEncodings())
{
var url = $"https://example.com?foo{c}bar=baz{c}quux";
var queryIndex = url.IndexOf('?');
var query = url.Substring(queryIndex + 1);
var oldParseResult = QueryHelpers.ParseNullableQuery(query);
oldParseResult.ShouldNotBeNull();
var oldNvc = oldParseResult.AsNameValueCollection();
var newNvc = url.ReadQueryStringAsNameValueCollection();
newNvc.ShouldBeEquivalentTo(oldNvc, "Failure for character or encoding:" + c);
}
}
[Fact]
[Trait("Category", Category)]
public void ReadQueryStringAsNameValueCollection_should_handle_duplicate_keys()