multipart/mixed ordered parts

Parse ordered multipart payloads where part order matters.

Full source

File: pwsh/tutorial/examples/22.4-Multipart-Mixed.ps1

<#!
    22.4 multipart/mixed (ordered parts)

    Client example (PowerShell):
        $boundary = 'mixed-boundary'
        $body = @(
            "--$boundary",
            "Content-Type: text/plain",
            "",
            "first",
            "--$boundary",
            "Content-Type: application/json",
            "",
            '{"value":42}',
            "--$boundary--",
            ""
        ) -join "`r`n"
        Invoke-RestMethod -Method Post -Uri "http://127.0.0.1:$Port/mixed" -ContentType "multipart/mixed; boundary=$boundary" -Body $body

    Cleanup:
        Remove-Item -Recurse -Force (Join-Path ([System.IO.Path]::GetTempPath()) 'kestrun-uploads-22.4-multipart-mixed')
#>
param(
    [int]$Port = 5000,
    [IPAddress]$IPAddress = [IPAddress]::Loopback
)

New-KrLogger |
    Set-KrLoggerLevel -Value Debug |
    Add-KrSinkConsole |
    Register-KrLogger -Name 'console' -SetAsDefault

New-KrServer -Name 'Forms 22.4'

Add-KrEndpoint -Port $Port -IPAddress $IPAddress | Out-Null

# Upload directory
$scriptName = [System.IO.Path]::GetFileNameWithoutExtension($PSCommandPath)
$uploadRoot = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath "kestrun-uploads-$scriptName"

# Add Rules
# Note: multipart/mixed is parsed as ordered parts. Rules apply when a part includes a Content-Disposition name.
# Opt-in: only multipart/form-data is enabled by default
New-KrFormPartRule -Name 'text' -MaxBytes 1024 |
    New-KrFormPartRule -Name 'json' -MaxBytes 1024 |
    Add-KrFormOption -DefaultUploadPath $uploadRoot -AllowedRequestContentTypes 'multipart/mixed' |
    Add-KrFormRoute -Pattern '/mixed' -ScriptBlock {
        $contentTypes = $FormPayload.Parts | ForEach-Object { $_.ContentType }
        Write-KrJsonResponse -InputObject @{ count = $FormPayload.Parts.Count; contentTypes = $contentTypes } -StatusCode 200
    }

Enable-KrConfiguration

# Start the server asynchronously
Start-KrServer

Step-by-step

  1. Logger: Enable console logging.
  2. Server: Create the host and bind a listener.
  3. Storage: Configure a temporary upload directory.
  4. Options: Opt in to multipart/mixed via AllowedRequestContentTypes.
  5. Route: Add /mixed with Add-KrFormRoute and return ordered content types and count.

Try it

$boundary = 'mixed-boundary'
$body = @(
    "--$boundary",
    "Content-Disposition: form-data; name=\"text\"",
    "Content-Type: text/plain",
    "",
    "first",
    "--$boundary",
    "Content-Disposition: form-data; name=\"json\"",
    "Content-Type: application/json",
    "",
    '{"value":42}',
    "--$boundary--",
    ""
) -join "`r`n"
Invoke-RestMethod -Method Post -Uri 'http://127.0.0.1:5000/mixed' -ContentType "multipart/mixed; boundary=$boundary" -Body $body
cat > payload.txt <<'EOF'
--mixed-boundary
Content-Disposition: form-data; name="text"
Content-Type: text/plain

first
--mixed-boundary
Content-Disposition: form-data; name="json"
Content-Type: application/json

{"value":42}
--mixed-boundary--
EOF

curl -X POST -H "Content-Type: multipart/mixed; boundary=mixed-boundary" --data-binary @payload.txt http://127.0.0.1:5000/mixed

Expected output

{
  "count": 2,
  "contentTypes": ["text/plain", "application/json"]
}

Notes

  • Content types: Add-KrFormRoute defaults to multipart/form-data only; this script opts in to multipart/mixed.
  • Limits: MaxParts constrains how many sections are processed.
  • Security: Parts are stored to disk without loading whole bodies into memory.
  • Logging: Per-part metadata is logged via host.Logger.
  • Rules: If you configure KrPartRule, include Content-Disposition: ...; name="..." so rules can match ordered parts.

Troubleshooting

  • 415 / Unsupported Content-Type: Ensure the request Content-Type is multipart/mixed; boundary=... and the route options allow it.
  • Unexpected part order: For ordered payloads, keep the body exactly in the intended sequence.

References


Previous / Next

Previous: application/x-www-form-urlencoded forms Next: Nested multipart/mixed