From c2934912af7667947c43c94599fead54eb0cf9fe Mon Sep 17 00:00:00 2001 From: Chris Ross Date: Tue, 16 Sep 2014 10:30:45 -0700 Subject: [PATCH] #121 - Make the query parsing API public. --- .../Infrastructure/ParsingHelpers.cs | 16 ----- .../QueryFeature.cs | 3 +- .../ParsingHelpers.cs | 17 +++++ .../QueryHelpers.cs | 11 ++++ .../QueryHelpersTests.cs | 64 +++++++++++++++++++ 5 files changed, 94 insertions(+), 17 deletions(-) create mode 100644 test/Microsoft.AspNet.WebUtilities.Tests/QueryHelpersTests.cs diff --git a/src/Microsoft.AspNet.PipelineCore/Infrastructure/ParsingHelpers.cs b/src/Microsoft.AspNet.PipelineCore/Infrastructure/ParsingHelpers.cs index f1db7fe748..10c9ef3672 100644 --- a/src/Microsoft.AspNet.PipelineCore/Infrastructure/ParsingHelpers.cs +++ b/src/Microsoft.AspNet.PipelineCore/Infrastructure/ParsingHelpers.cs @@ -795,22 +795,6 @@ namespace Microsoft.AspNet.PipelineCore.Infrastructure } }; - private static readonly char[] AmpersandAndSemicolon = new[] { '&', ';' }; - - internal static IDictionary GetQuery(string queryString) - { - if (!string.IsNullOrEmpty(queryString) && queryString[0] == '?') - { - queryString = queryString.Substring(1); - } - var accumulator = new Dictionary>(StringComparer.OrdinalIgnoreCase); - ParseDelimited(queryString, AmpersandAndSemicolon, AppendItemCallback, accumulator); - return accumulator.ToDictionary( - item => item.Key, - item => item.Value.ToArray(), - StringComparer.OrdinalIgnoreCase); - } - internal static string GetJoinedValue(IDictionary store, string key) { string[] values = GetUnmodifiedValues(store, key); diff --git a/src/Microsoft.AspNet.PipelineCore/QueryFeature.cs b/src/Microsoft.AspNet.PipelineCore/QueryFeature.cs index ab5ea5157a..5106808d03 100644 --- a/src/Microsoft.AspNet.PipelineCore/QueryFeature.cs +++ b/src/Microsoft.AspNet.PipelineCore/QueryFeature.cs @@ -6,6 +6,7 @@ using Microsoft.AspNet.FeatureModel; using Microsoft.AspNet.Http; using Microsoft.AspNet.HttpFeature; using Microsoft.AspNet.PipelineCore.Infrastructure; +using Microsoft.AspNet.WebUtilities; using Microsoft.AspNet.WebUtilities.Collections; namespace Microsoft.AspNet.PipelineCore @@ -45,7 +46,7 @@ namespace Microsoft.AspNet.PipelineCore if (_query == null || _queryString != queryString) { _queryString = queryString; - _query = new ReadableStringCollection(ParsingHelpers.GetQuery(queryString)); + _query = QueryHelpers.ParseQuery(queryString); } return _query; } diff --git a/src/Microsoft.AspNet.WebUtilities/ParsingHelpers.cs b/src/Microsoft.AspNet.WebUtilities/ParsingHelpers.cs index e5dbd65a9c..79f9d22ebd 100644 --- a/src/Microsoft.AspNet.WebUtilities/ParsingHelpers.cs +++ b/src/Microsoft.AspNet.WebUtilities/ParsingHelpers.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.AspNet.Http; using Microsoft.AspNet.WebUtilities.Collections; @@ -90,5 +91,21 @@ namespace Microsoft.AspNet.WebUtilities string[] values; return store.TryGetValue(key, out values) ? values : null; } + + private static readonly char[] AmpersandAndSemicolon = new[] { '&', ';' }; + + internal static IReadableStringCollection GetQuery(string queryString) + { + if (!string.IsNullOrEmpty(queryString) && queryString[0] == '?') + { + queryString = queryString.Substring(1); + } + var accumulator = new Dictionary>(StringComparer.OrdinalIgnoreCase); + ParseDelimited(queryString, AmpersandAndSemicolon, AppendItemCallback, accumulator); + return new ReadableStringCollection(accumulator.ToDictionary( + item => item.Key, + item => item.Value.ToArray(), + StringComparer.OrdinalIgnoreCase)); + } } } diff --git a/src/Microsoft.AspNet.WebUtilities/QueryHelpers.cs b/src/Microsoft.AspNet.WebUtilities/QueryHelpers.cs index d31a250fdb..f54ff50dea 100644 --- a/src/Microsoft.AspNet.WebUtilities/QueryHelpers.cs +++ b/src/Microsoft.AspNet.WebUtilities/QueryHelpers.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Text; +using Microsoft.AspNet.Http; namespace Microsoft.AspNet.WebUtilities { @@ -43,5 +44,15 @@ namespace Microsoft.AspNet.WebUtilities } return sb.ToString(); } + + /// + /// Parse a query string into its component key and value parts. + /// + /// The raw query string value, with or without the leading '?'. + /// A collection of parsed keys and values. + public static IReadableStringCollection ParseQuery(string text) + { + return ParsingHelpers.GetQuery(text); + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNet.WebUtilities.Tests/QueryHelpersTests.cs b/test/Microsoft.AspNet.WebUtilities.Tests/QueryHelpersTests.cs new file mode 100644 index 0000000000..3c81820e3e --- /dev/null +++ b/test/Microsoft.AspNet.WebUtilities.Tests/QueryHelpersTests.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Xunit; + +namespace Microsoft.AspNet.WebUtilities +{ + public class QueryHelperTests + { + [Fact] + public void ParseQueryWithUniqueKeysWorks() + { + var collection = QueryHelpers.ParseQuery("?key1=value1&key2=value2"); + Assert.Equal(2, collection.Count); + Assert.Equal("value1", collection["key1"]); + Assert.Equal("value2", collection["key2"]); + } + + [Fact] + public void ParseQueryWithoutQuestionmarkWorks() + { + var collection = QueryHelpers.ParseQuery("key1=value1&key2=value2"); + Assert.Equal(2, collection.Count); + Assert.Equal("value1", collection["key1"]); + Assert.Equal("value2", collection["key2"]); + } + + [Fact] + public void ParseQueryWithDuplicateKeysGroups() + { + var collection = QueryHelpers.ParseQuery("?key1=valueA&key2=valueB&key1=valueC"); + Assert.Equal(2, collection.Count); + Assert.Equal("valueA,valueC", collection["key1"]); + Assert.Equal("valueB", collection["key2"]); + } + + [Fact] + public void ParseQueryWithSemicolonWorks() + { + var collection = QueryHelpers.ParseQuery("?key1=value1;key2=value2"); + Assert.Equal(2, collection.Count); + Assert.Equal("value1", collection["key1"]); + Assert.Equal("value2", collection["key2"]); + } + + [Fact] + public void ParseQueryWithEmptyValuesWorks() + { + var collection = QueryHelpers.ParseQuery("?key1=;key2="); + Assert.Equal(2, collection.Count); + Assert.Equal(string.Empty, collection["key1"]); + Assert.Equal(string.Empty, collection["key2"]); + } + + [Fact] + public void ParseQueryWithEmptyKeyWorks() + { + var collection = QueryHelpers.ParseQuery("?=value1;="); + Assert.Equal(1, collection.Count); + Assert.Equal("value1,", collection[""]); + } + } +} \ No newline at end of file