Repository: PowerShell/PowerShell
File: src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs (C#)

Code before:
            }

            // Treat Strings and other single values as a StringContent.
            // If enumeration is false, also treat IEnumerables as StringContents.
            // String implements IEnumerable so the explicit check is required.
            if (!enumerate || fieldValue is string || fieldValue is not IEnumerable)
            {
                formData.Add(GetMultipartStringContent(fieldName: fieldName, fieldValue: fieldValue));
                return;
            }

            // Treat the value as a collection and enumerate it if enumeration is true
            if (enumerate && fieldValue is IEnumerable items)
            {
                foreach (object item in items)
                {
                    // Recurse, but do not enumerate the next level. IEnumerables will be treated as single values.
                    AddMultipartContent(fieldName: fieldName, fieldValue: item, formData: formData, enumerate: false);
                }
            }
        }

        /// <summary>
        /// Gets a <see cref="StringContent"/> from the supplied field name and field value. Uses <see cref="LanguagePrimitives.ConvertTo{T}(object)"/> to convert the objects to strings.
        /// </summary>
        /// <param name="fieldName">The Field Name to use for the <see cref="StringContent"/></param>
        /// <param name="fieldValue">The Field Value to use for the <see cref="StringContent"/></param>
        private static StringContent GetMultipartStringContent(object fieldName, object fieldValue)
        {
            ContentDispositionHeaderValue contentDisposition = new("form-data");

            // .NET does not enclose field names in quotes, however, modern browsers and curl do.
            contentDisposition.Name = "\"" + LanguagePrimitives.ConvertTo<string>(fieldName) + "\"";

            StringContent result = new(LanguagePrimitives.ConvertTo<string>(fieldValue));
            result.Headers.ContentDisposition = contentDisposition;

            return result;
        }

        /// <summary>
        /// Gets a <see cref="StreamContent"/> from the supplied field name and <see cref="Stream"/>. Uses <see cref="LanguagePrimitives.ConvertTo{T}(object)"/> to convert the fieldname to a string.
        /// </summary>
        /// <param name="fieldName">The Field Name to use for the <see cref="StreamContent"/></param>
        /// <param name="stream">The <see cref="Stream"/> to use for the <see cref="StreamContent"/></param>
        private static StreamContent GetMultipartStreamContent(object fieldName, Stream stream)
        {
            ContentDispositionHeaderValue contentDisposition = new("form-data");

            // .NET does not enclose field names in quotes, however, modern browsers and curl do.
            contentDisposition.Name = "\"" + LanguagePrimitives.ConvertTo<string>(fieldName) + "\"";

Review comment (suggestion):
This broke IWR for us when we upgraded to 7.4.  We use IWR in a publish script to upload a zip to an internal PERL / CGI-based website.  AFAICT the PERL CGI module requires field name values to be quoted.  See https://github.com/PowerShell/PowerShell/issues/23843

Code after:
            if (!enumerate || fieldValue is string || fieldValue is not IEnumerable)
            {
                formData.Add(GetMultipartStringContent(fieldName: fieldName, fieldValue: fieldValue));
                return;
            }

            // Treat the value as a collection and enumerate it if enumeration is true
            if (enumerate && fieldValue is IEnumerable items)
            {
                foreach (object item in items)
                {
                    // Recurse, but do not enumerate the next level. IEnumerables will be treated as single values.
                    AddMultipartContent(fieldName: fieldName, fieldValue: item, formData: formData, enumerate: false);
                }
            }
        }

        /// <summary>
        /// Gets a <see cref="StringContent"/> from the supplied field name and field value. Uses <see cref="LanguagePrimitives.ConvertTo{T}(object)"/> to convert the objects to strings.
        /// </summary>
        /// <param name="fieldName">The Field Name to use for the <see cref="StringContent"/></param>
        /// <param name="fieldValue">The Field Value to use for the <see cref="StringContent"/></param>
        private static StringContent GetMultipartStringContent(object fieldName, object fieldValue)
        {
            ContentDispositionHeaderValue contentDisposition = new("form-data");
            contentDisposition.Name = LanguagePrimitives.ConvertTo<string>(fieldName);

            StringContent result = new(LanguagePrimitives.ConvertTo<string>(fieldValue));
            result.Headers.ContentDisposition = contentDisposition;

            return result;
        }

        /// <summary>
        /// Gets a <see cref="StreamContent"/> from the supplied field name and <see cref="Stream"/>. Uses <see cref="LanguagePrimitives.ConvertTo{T}(object)"/> to convert the fieldname to a string.
        /// </summary>
        /// <param name="fieldName">The Field Name to use for the <see cref="StreamContent"/></param>
        /// <param name="stream">The <see cref="Stream"/> to use for the <see cref="StreamContent"/></param>
        private static StreamContent GetMultipartStreamContent(object fieldName, Stream stream)
        {
            ContentDispositionHeaderValue contentDisposition = new("form-data");
            contentDisposition.Name = LanguagePrimitives.ConvertTo<string>(fieldName);

            StreamContent result = new(stream);
            result.Headers.ContentDisposition = contentDisposition;
            result.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

            return result;
        }

        /// <summary>