API Fetch: Respect caller-provided Content-Type in httpV1 middleware#76285
Conversation
…ware The httpV1 middleware unconditionally overwrites the Content-Type header to 'application/json' for PUT, PATCH, and DELETE requests. Because the spread order places Content-Type after ...options.headers, any custom Content-Type set by the caller is silently lost. This makes it impossible to send non-JSON content (e.g. raw markdown, plain text) via apiFetch for these HTTP methods. The server receives raw content with a JSON content type and returns 400 Invalid JSON body. Fix: Reorder the spread so Content-Type: application/json acts as a default that callers can override, while X-HTTP-Method-Override remains forced after the spread (cannot be overridden by callers). Also adds 11 new tests covering Content-Type preservation, default behavior, all override methods, header preservation, and body integrity. Fixes WordPress#76284
|
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
👋 Thanks for your first Pull Request and for helping build the future of Gutenberg and WordPress, @chubes4! In case you missed it, we'd love to have you join us in our Slack community. If you want to learn more about WordPress development in general, check out the Core Handbook full of helpful information. |
What?
Reorders the header spread in
httpV1Middlewareso thatContent-Type: application/jsonacts as a default rather than an unconditional overwrite. Caller-providedContent-Typeheaders are now preserved.Why?
The middleware currently places
Content-Type: 'application/json'after...options.headersin the spread, which means any custom Content-Type set by the caller is silently lost:This makes it impossible to send non-JSON content (e.g. raw markdown, plain text, XML) via
apiFetchusing PUT, PATCH, or DELETE methods. The server receives raw content labeled asapplication/jsonand returns400 Invalid JSON body passed.How?
One-line reorder — move
Content-Typebefore the caller's header spread so it acts as a default:application/json(backwards compatible)X-HTTP-Method-Overrideremains after the spread and cannot be overridden by callers (correct security behavior)How was this discovered?
While building a WordPress plugin (Data Machine) that saves markdown files (agent memory files —
SOUL.md,MEMORY.md) via a custom REST endpoint. The admin page usedapiFetchwithmethod: 'PUT',headers: { 'Content-Type': 'text/plain' }, andbody: rawMarkdownContent. Saves failed with400 Invalid JSON body passedbecause the middleware silently overwrote the Content-Type.Testing
Automated
packages/api-fetch/src/middlewares/test/http-v1.ts@wordpress/api-fetchpackage pass (9 suites, 0 failures)Manual (production site)
api-fetch.min.json a live WordPress 6.9 site (chubes.net)Content-Type: text/plainsurvives through the middleware to the server (verified via Chrome DevTools Request Headers)apiFetchX-HTTP-Method-Override: PUTis still correctly set and forcedFixes #76284
AI Disclosure
Per the WordPress AI Guidelines, this contribution was made with meaningful AI assistance:
httpV1Middlewaresource code, identify the spread order issue, propose the fix, write the comprehensive test suite, and draft the PR description. The fix itself (reordering one line) was straightforward once the root cause was identified.script-loader-packages.min.php), and confirming the fix with real browser requests saving real markdown files.