diff options
Diffstat (limited to 'src/Mvc/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/FormDataCollectionExtensions.cs')
-rw-r--r-- | src/Mvc/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/FormDataCollectionExtensions.cs | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/src/Mvc/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/FormDataCollectionExtensions.cs b/src/Mvc/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/FormDataCollectionExtensions.cs new file mode 100644 index 0000000000..a9539e83a0 --- /dev/null +++ b/src/Mvc/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/FormDataCollectionExtensions.cs @@ -0,0 +1,114 @@ +// 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.Net.Http.Formatting; +using System.Text; + +namespace Microsoft.AspNetCore.Mvc.WebApiCompatShim +{ + public static class FormDataCollectionExtensions + { + // This is a helper method to use Model Binding over a JQuery syntax. + // Normalize from JQuery to MVC keys. The model binding infrastructure uses MVC keys + // x[] --> x + // [] --> "" + // x[field] --> x.field, where field is not a number + public static string NormalizeJQueryToMvc(string key) + { + if (key == null) + { + return string.Empty; + } + + StringBuilder sb = null; + var i = 0; + while (true) + { + var indexOpen = key.IndexOf('[', i); + if (indexOpen < 0) + { + // Fast path, no normalization needed. + // This skips the string conversion and allocating the string builder. + if (i == 0) + { + return key; + } + sb = sb ?? new StringBuilder(); + sb.Append(key, i, key.Length - i); + break; // no more brackets + } + + sb = sb ?? new StringBuilder(); + sb.Append(key, i, indexOpen - i); // everything up to "[" + + // Find closing bracket. + var indexClose = key.IndexOf(']', indexOpen); + if (indexClose == -1) + { + throw new ArgumentException(Resources.JQuerySyntaxMissingClosingBracket, nameof(key)); + } + + if (indexClose == indexOpen + 1) + { + // Empty bracket. Signifies array. Just remove. + } + else + { + if (char.IsDigit(key[indexOpen + 1])) + { + // array index. Leave unchanged. + sb.Append(key, indexOpen, indexClose - indexOpen + 1); + } + else + { + // Field name. Convert to dot notation. + sb.Append('.'); + sb.Append(key, indexOpen + 1, indexClose - indexOpen - 1); + } + } + + i = indexClose + 1; + if (i >= key.Length) + { + break; // end of string + } + } + return sb.ToString(); + } + + public static IEnumerable<KeyValuePair<string, string>> GetJQueryNameValuePairs( + this FormDataCollection formData) + { + if (formData == null) + { + throw new ArgumentNullException(nameof(formData)); + } + + var count = 0; + + foreach (var kv in formData) + { + ThrowIfMaxHttpCollectionKeysExceeded(count); + + var key = NormalizeJQueryToMvc(kv.Key); + var value = kv.Value ?? string.Empty; + yield return new KeyValuePair<string, string>(key, value); + + count++; + } + } + + private static void ThrowIfMaxHttpCollectionKeysExceeded(int count) + { + if (count >= MediaTypeFormatter.MaxHttpCollectionKeys) + { + var message = Resources.FormatMaxHttpCollectionKeyLimitReached( + MediaTypeFormatter.MaxHttpCollectionKeys, + typeof(MediaTypeFormatter)); + throw new InvalidOperationException(message); + } + } + } +} |