求推荐C#轻量库:将HTTP POST Multipart数据解析为对象/数组树
Absolutely! There are a couple of great options in C# for parsing multipart POST data and converting that flat form field structure into a nested tree of objects, arrays, and primitive values—just like how PHP handles $_POST automatically. Let’s break down your best choices:
1. ASP.NET Core: Built-in Support (No Extra Libraries Needed)
If you’re working in an ASP.NET Core project, the framework’s native model binding already handles PHP-style form naming out of the box.
First, define a model that matches your expected nested structure:
public class SubmittedForm { public bool Boolean { get; set; } public int Integer { get; set; } public string String { get; set; } public List<object> Array { get; set; } public NestedObject Object { get; set; } } public class NestedObject { public bool Boolean { get; set; } public int Integer { get; set; } public string String { get; set; } }
Then, in your controller action, just accept the model directly—ASP.NET Core will automatically unflatten the form data for you:
[HttpPost] public IActionResult ProcessForm(SubmittedForm formData) { // formData is already your nested tree structure! return Ok(formData); }
If you need to manually parse multipart data (e.g., alongside file uploads), use the MultipartReader from Microsoft.AspNetCore.WebUtilities:
using Microsoft.AspNetCore.WebUtilities; using Microsoft.Net.Http.Headers; var boundary = MediaTypeHeaderValue.Parse(Request.ContentType).Boundary.Value; var reader = new MultipartReader(boundary, Request.Body); var section = await reader.ReadNextSectionAsync(); var flatFormData = new Dictionary<string, string>(); while (section != null) { var contentDisposition = ContentDispositionHeaderValue.Parse(section.ContentDisposition); if (!contentDisposition.FileName.HasValue) { // This is a form field, not a file var fieldName = contentDisposition.Name.Trim('"'); var fieldValue = await section.ReadAsStringAsync(); flatFormData.Add(fieldName, fieldValue); } section = await reader.ReadNextSectionAsync(); } // Use the unflattening logic below to convert flatFormData to nested structure
2. General .NET Projects: HttpMultipartParser
For non-ASP.NET Core projects (like console apps or desktop apps), the lightweight HttpMultipartParser library makes parsing multipart data straightforward.
First, install it via NuGet:
Install-Package HttpMultipartParser
Then parse the data and unflatten it:
using HttpMultipartParser; // Assume you have a Stream containing the multipart request data var parser = await MultipartFormDataParser.ParseAsync(requestStream); var flatFormData = parser.Parameters.ToDictionary(p => p.Name, p => p.Data); // Convert the flat data to your nested tree structure var nestedData = UnflattenFormData(flatFormData);
3. DIY Unflattening Logic (If You Prefer No Libraries)
If you want full control, here’s a custom implementation that handles both array[] (auto-appended arrays) and object[key] (nested objects) syntax, matching PHP’s behavior:
public static object UnflattenFormData(Dictionary<string, string> flatData) { var result = new Dictionary<string, object>(); foreach (var kvp in flatData) { var keyParts = ParseFormKey(kvp.Key); var currentContainer = result as IDictionary<string, object>; object parentContainer = null; for (int i = 0; i < keyParts.Count; i++) { var part = keyParts[i]; if (part.IsArray) { // Handle array fields (array[] or array[0]) if (!currentContainer.ContainsKey(part.BaseName) || !(currentContainer[part.BaseName] is List<object> array)) { array = new List<object>(); currentContainer[part.BaseName] = array; } else { array = currentContainer[part.BaseName] as List<object>; } if (part.Index.HasValue) { // Ensure array has enough elements for the specified index while (array.Count <= part.Index.Value) array.Add(null); array[part.Index.Value] = ParseValue(kvp.Value); } else { // Append to array (for array[] syntax) array.Add(ParseValue(kvp.Value)); } parentContainer = array; currentContainer = null; // Arrays can't be nested object containers } else { // Handle nested object fields (object[key]) if (!currentContainer.ContainsKey(part.BaseName) || !(currentContainer[part.BaseName] is IDictionary<string, object> nestedObj)) { nestedObj = new Dictionary<string, object>(); currentContainer[part.BaseName] = nestedObj; } else { nestedObj = currentContainer[part.BaseName] as IDictionary<string, object>; } if (i == keyParts.Count - 1) { // Final part: set the value nestedObj[part.BaseName] = ParseValue(kvp.Value); } else { // Move to the nested object container parentContainer = currentContainer; currentContainer = nestedObj; } } } } return result; } // Helper to parse form keys like "array[]" or "object[boolean]" private static List<KeyPart> ParseFormKey(string key) { var parts = new List<KeyPart>(); var currentName = new StringBuilder(); int i = 0; while (i < key.Length) { if (key[i] == '[') { if (currentName.Length > 0) { parts.Add(new KeyPart { BaseName = currentName.ToString() }); currentName.Clear(); } i++; var insideBrackets = new StringBuilder(); while (i < key.Length && key[i] != ']') { insideBrackets.Append(key[i]); i++; } if (insideBrackets.Length == 0) { // Mark as array (array[]) parts.Last().IsArray = true; } else if (int.TryParse(insideBrackets.ToString(), out int idx)) { // Mark as array with specific index (array[2]) parts.Last().IsArray = true; parts.Last().Index = idx; } else { // Nested object key (object[key]) parts.Add(new KeyPart { BaseName = insideBrackets.ToString() }); } i++; // Skip closing ] } else { currentName.Append(key[i]); i++; } } if (currentName.Length > 0) { parts.Add(new KeyPart { BaseName = currentName.ToString() }); } return parts; } // Helper to convert string values to appropriate types (bool, int, etc.) private static object ParseValue(string value) { if (bool.TryParse(value, out bool boolVal)) return boolVal; if (int.TryParse(value, out int intVal)) return intVal; if (double.TryParse(value, out double doubleVal)) return doubleVal; return value; } // Helper class for parsed key parts private class KeyPart { public string BaseName { get; set; } public bool IsArray { get; set; } public int? Index { get; set; } }
内容的提问来源于stack exchange,提问作者Virus721

