// 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.Text; using System.Text.Encodings.Web; using Microsoft.Extensions.Primitives; namespace Microsoft.AspNet.WebUtilities { public static class QueryHelpers { /// /// Append the given query key and value to the URI. /// /// The base URI. /// The name of the query key. /// The query value. /// The combined result. public static string AddQueryString(string uri, string name, string value) { if (uri == null) { throw new ArgumentNullException(nameof(uri)); } if (name == null) { throw new ArgumentNullException(nameof(name)); } if (value == null) { throw new ArgumentNullException(nameof(value)); } return AddQueryString( uri, new[] { new KeyValuePair(name, value) }); } /// /// Append the given query keys and values to the uri. /// /// The base uri. /// A collection of name value query pairs to append. /// The combined result. public static string AddQueryString(string uri, IDictionary queryString) { if (uri == null) { throw new ArgumentNullException(nameof(uri)); } if (queryString == null) { throw new ArgumentNullException(nameof(queryString)); } return AddQueryString(uri, (IEnumerable>)queryString); } private static string AddQueryString( string uri, IEnumerable> queryString) { if (uri == null) { throw new ArgumentNullException(nameof(uri)); } if (queryString == null) { throw new ArgumentNullException(nameof(queryString)); } var anchorIndex = uri.IndexOf('#'); var uriToBeAppended = uri; var anchorText = ""; // If there is an anchor, then the query string must be inserted before its first occurance. if (anchorIndex != -1) { anchorText = uri.Substring(anchorIndex); uriToBeAppended = uri.Substring(0, anchorIndex); } var queryIndex = uriToBeAppended.IndexOf('?'); var hasQuery = queryIndex != -1; var sb = new StringBuilder(); sb.Append(uriToBeAppended); foreach (var parameter in queryString) { sb.Append(hasQuery ? '&' : '?'); sb.Append(UrlEncoder.Default.Encode(parameter.Key)); sb.Append('='); sb.Append(UrlEncoder.Default.Encode(parameter.Value)); hasQuery = true; } sb.Append(anchorText); 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 IDictionary ParseQuery(string queryString) { if (!string.IsNullOrEmpty(queryString) && queryString[0] == '?') { queryString = queryString.Substring(1); } var accumulator = new KeyValueAccumulator(); int textLength = queryString.Length; int equalIndex = queryString.IndexOf('='); if (equalIndex == -1) { equalIndex = textLength; } int scanIndex = 0; while (scanIndex < textLength) { int delimiterIndex = queryString.IndexOf('&', scanIndex); if (delimiterIndex == -1) { delimiterIndex = textLength; } if (equalIndex < delimiterIndex) { while (scanIndex != equalIndex && char.IsWhiteSpace(queryString[scanIndex])) { ++scanIndex; } string name = queryString.Substring(scanIndex, equalIndex - scanIndex); string value = queryString.Substring(equalIndex + 1, delimiterIndex - equalIndex - 1); accumulator.Append( Uri.UnescapeDataString(name.Replace('+', ' ')), Uri.UnescapeDataString(value.Replace('+', ' '))); equalIndex = queryString.IndexOf('=', delimiterIndex); if (equalIndex == -1) { equalIndex = textLength; } } scanIndex = delimiterIndex + 1; } return accumulator.GetResults(); } } }