Skip to content

multipart/form-data bodies with file parts #30

Closed

Description

@dataGriff

Why

Uploads are the most common thing a plain .http file cannot express today. REST Client and JetBrains both accept a multipart body written out with the boundary and < ./file inside a part; apic sends it as a literal string, so the file reference goes over the wire as text. The cookbook's "uploads" recipe is limited to a whole-body < file for the same reason.

What

Support the editor syntax as it stands:

POST {{baseUrl}}/upload
Content-Type: multipart/form-data; boundary=WebAppBoundary

--WebAppBoundary
Content-Disposition: form-data; name="title"

Quarterly report
--WebAppBoundary
Content-Disposition: form-data; name="file"; filename="report.pdf"
Content-Type: application/pdf

< ./report.pdf
--WebAppBoundary--

Acceptance criteria:

  • When Content-Type is multipart/form-data, the body is parsed into parts; a < path or <@ path line inside a part is replaced by the file's bytes (templated for <@). Paths are relative to the .http file and confined to the project root exactly like bodyFilePath (internal/runner/runner.go:817-845).
  • The body is assembled binary-safe (a []byte, not a string round-tripped through templates). Content-Length is set.
  • {{variables}} in text parts resolve as usual.
  • apic validate reports a missing part file as an error, like it does for a whole-body file.
  • apic curl maps parts onto -F name=value / -F file=@path.
  • --json shows the request body as "<multipart: 2 parts, 1 file>" rather than the bytes; --redact behaves as for other bodies.
  • The OpenAPI importer generates a multipart skeleton for multipart/form-data request bodies instead of skipping them (internal/openapi/import.go:498-500).

Where

  • internal/httpfile/parse.go (body capture at 189-203), ast.go (Request.Body)
  • internal/runner/runner.go (body construction 289-310, send 520-523)
  • internal/curlexport/curl.go
  • internal/openapi/import.go

Docs

docs/format.md (Structure table, new "Multipart" section), docs/cookbook.md (replace "Send a file as the body" with an upload recipe), docs/comparison.md table.

Tests

Parser case in internal/httpfile/testdata/sample.http; runner test with httptest reading the request via multipart.NewReader and checking part names, filenames and bytes; validate test for a missing part file; curl export test.

Activity

  1. dataGriff commented on Sep 20, 2026

    @dataGriff
    OwnerAuthor

    Shipped in #103: a multipart/form-data body is read into parts the way the editors write it, < ./file parts send the file's bytes (binary-safe, Content-Length set, confined to the project root), text parts are templates, apic validate reports bad-multipart and missing part files, apic curl maps parts onto --form-string and -F, --json shows <multipart: 2 parts, 1 file>, and the OpenAPI importer generates a skeleton with a placeholder file per binary property. Docs: format (Multipart uploads), cookbook (Upload a file). The demo project has no upload request yet; that can ride the lesson that needs it.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions