kit
kit copied to clipboard
Formdata send by Sveltekit Endpoint is send by plain/text with no data
Describe the bug
SvelteKit:
If you try to send a POST Request from Server-to-Server with FormData the request will fail.
Sveltekits fetch() does not set content-type multipart/form-data. The Request will be text/plain and no Formdata will be submitted
This probably relates to #5349 #5292
I tried my best and created an Express Server and a Sveltekit App (see below) for debugging.
export const POST: RequestHandler = async ({ request }) => {
// const formDataSendbyFrontend = await request.formData();
const demoData = new FormData();
demoData.append('data', "lorem ipsum");
const res = await fetch('https://sveltekit-express-bug-tester.herokuapp.com/formdata', {
method: 'POST',
body: demoData
})
The Request will fail, because Sveltekit sends text/plain and no FormData (???)
// Req Object Express Server
{
body: {
headers: {
host: 'sveltekit-express-bug-tester.herokuapp.com',
connection: 'close',
accept: '*/*',
'content-type': 'text/plain;charset=UTF-8',
'accept-language': '*',
'sec-fetch-mode': 'cors',
'user-agent': 'undici',
'accept-encoding': 'br, gzip, deflate',
'x-request-id': '4f31b926-31c7-4cd2-a612-0bf85edb01fe',
'x-forwarded-for': '1.1.1.1',
'x-forwarded-proto': 'https',
'x-forwarded-port': '443',
via: '1.1 vegur',
'connect-time': '0',
'x-request-start': '1658828491278',
'total-route-time': '0',
'content-length': '17'
},
error: 'Not multipart/form-data'
}
}
This used to Work with node-fetch and Sveltekit v.350 (not sure which version it got broken)
Reproduction
I prepared to Repos with a Server (Express + Multer) and a minimal Sveltekit App
💻 Server Repo: https://github.com/garytube/sveltekit-formdata-bug-server 🌍 Sveltekit Repo: https://github.com/garytube/sveltekit-formdata-bug-sveltekit
Send a POST Request with FormData from a SvelteKit Endpoint to http://sveltekit-express-bug-tester.herokuapp.com/formdata
const demoData = new FormData();
demoData.append('data', "lorem ipsum");
const res = await fetch('http://sveltekit-express-bug-tester.herokuapp.com/formdata', {
method: 'POST',
body: demoData
})
/formdata can also recive a File (Fieldname: image) (can be anything txt, jpg, pdf up to 1mb)
Maybe this helps debugging
<input bind:files type="file" accept="image/*" />
formData.append('image', files[0]')
The Sveltekit Demo Project will
- Send Form Data to Sveltekit Endpoint
- Sveltekit Endpoint will forward Request TO OFF-SITE Server
- Request will fail because req.content-type of Sveltekit to OFF-SITE server is text/plain and formdata is missing
Logs
REQUEST LOG EXPRESS SERVER
2022-07-26T10:52:17.994877+00:00 app[web.1]: 415 {
2022-07-26T10:52:17.994884+00:00 app[web.1]: headers: {
2022-07-26T10:52:17.994885+00:00 app[web.1]: host: 'sveltekit-express-bug-tester.herokuapp.com',
2022-07-26T10:52:17.994886+00:00 app[web.1]: connection: 'close',
2022-07-26T10:52:17.994887+00:00 app[web.1]: 'content-type': 'text/plain;charset=UTF-8',
2022-07-26T10:52:17.994887+00:00 app[web.1]: accept: '*/*',
2022-07-26T10:52:17.994888+00:00 app[web.1]: 'accept-language': '*',
2022-07-26T10:52:17.994888+00:00 app[web.1]: 'sec-fetch-mode': 'cors',
2022-07-26T10:52:17.994888+00:00 app[web.1]: 'user-agent': 'undici',
2022-07-26T10:52:17.994889+00:00 app[web.1]: 'accept-encoding': 'br, gzip, deflate',
2022-07-26T10:52:17.994889+00:00 app[web.1]: 'x-request-id': '47463367-0ee3-4c57-b58a-cbdbf0d7cf68',
2022-07-26T10:52:17.994889+00:00 app[web.1]: 'x-forwarded-for': '-------------',
2022-07-26T10:52:17.994890+00:00 app[web.1]: 'x-forwarded-proto': 'https',
2022-07-26T10:52:17.994890+00:00 app[web.1]: 'x-forwarded-port': '443',
2022-07-26T10:52:17.994890+00:00 app[web.1]: via: '1.1 vegur',
2022-07-26T10:52:17.994890+00:00 app[web.1]: 'connect-time': '0',
2022-07-26T10:52:17.994891+00:00 app[web.1]: 'x-request-start': '1658832737992',
2022-07-26T10:52:17.994891+00:00 app[web.1]: 'total-route-time': '0',
2022-07-26T10:52:17.994891+00:00 app[web.1]: 'content-length': '17'
2022-07-26T10:52:17.994892+00:00 app[web.1]: },
2022-07-26T10:52:17.994892+00:00 app[web.1]: file: undefined,
2022-07-26T10:52:17.994892+00:00 app[web.1]: body: undefined
2022-07-26T10:52:17.994892+00:00 app[web.1]: }
System Info
System:
OS: Windows 11
CPU: (12) x64 Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
Memory: 1.79 GB / 15.70 GB
Binaries:
Node: 18.1.0 - C:\Program Files\nodejs\node.EXE
npm: 8.15.0 - C:\Program Files\nodejs\npm.CMD
Browsers:
Chrome: 103.0.5060.134
Edge: Spartan (44.22000.120.0), Chromium (103.0.1264.71)
Internet Explorer: 11.0.22000.120
npmPackages:
@sveltejs/adapter-auto: next => 1.0.0-next.63
@sveltejs/kit: next => 1.0.0-next.394
svelte: ^3.46.0 => 3.49.0
vite: ^3.0.0 => 3.0.3
Severity
blocking an upgrade
Additional Information
Background: My Realworld App is public Facing but only the Sveltekit Server is allowed to talk to the "Secure" API. So instead of directly calling the "Secure" API from the Form, I send everything through the Sveltekit Server. This is why I need to send FromData Server-to-Server.
Used to work until undici came along ;)
YES, I'm having the same exact issue. I've managed to use getReader() because it was telling me that it was a Stream and I can get the data, but everything I send is corrupted. Your mention of ASCII solves a mystery for me as the file size I could get closest to was ASCII via StringDecoder(). I assumed everything was/is UTF8.
my workaround is to import node-fetch and replace undici in my POST Endpoints
import nodeFetch from 'node-fetch';
const res = await nodeFetch('https://sveltekit-express-bug-tester.herokuapp.com/formdata', {
method: 'POST',
body: formDataFromFrontend
});
PROPPER MULITPART CONTENT TYPE ⭐⭐⭐⭐⭐
2022-07-26T11:45:22.308100+00:00 app[web.1]: {
2022-07-26T11:45:22.308112+00:00 app[web.1]: headers: {
2022-07-26T11:45:22.308115+00:00 app[web.1]: host: 'sveltekit-express-bug-tester.herokuapp.com',
2022-07-26T11:45:22.308116+00:00 app[web.1]: connection: 'close',
2022-07-26T11:45:22.308116+00:00 app[web.1]: accept: '*/*',
2022-07-26T11:45:22.308117+00:00 app[web.1]: 'accept-encoding': 'gzip, deflate, br',
2022-07-26T11:45:22.308118+00:00 app[web.1]: 'content-type': 'multipart/form-data; boundary=----3734578923909508881256374353',
2022-07-26T11:45:22.308118+00:00 app[web.1]: 'user-agent': 'node-fetch',
2022-07-26T11:45:22.308119+00:00 app[web.1]: 'x-request-id': '7f3578c7-6963-4098-a193-7bf762561f37',
2022-07-26T11:45:22.308119+00:00 app[web.1]: 'x-forwarded-for': '------------',
2022-07-26T11:45:22.308119+00:00 app[web.1]: 'x-forwarded-proto': 'https',
2022-07-26T11:45:22.308120+00:00 app[web.1]: 'x-forwarded-port': '443',
2022-07-26T11:45:22.308120+00:00 app[web.1]: via: '1.1 vegur',
2022-07-26T11:45:22.308120+00:00 app[web.1]: 'connect-time': '0',
2022-07-26T11:45:22.308120+00:00 app[web.1]: 'x-request-start': '1658835922308',
2022-07-26T11:45:22.308121+00:00 app[web.1]: 'total-route-time': '0',
2022-07-26T11:45:22.308121+00:00 app[web.1]: 'content-length': '218'
2022-07-26T11:45:22.308121+00:00 app[web.1]: },
2022-07-26T11:45:22.308122+00:00 app[web.1]: file: undefined,
2022-07-26T11:45:22.308122+00:00 app[web.1]: body: [Object: null prototype] { text1: 'Hello', text2: 'World' }
it's upstream (Undici) issue, so we can only wait until NodeJS will fix this. There is also related issue https://github.com/nodejs/undici/issues/974
In my opinion, this issue is not related to multipart parsing. But still this is an undici bug.
import * as undici from 'undici';
import * as nodeFetch from 'node-fetch';
function contentType({ FormData, Request }) {
const body = new FormData();
body.set('name', 'value');
const request = new Request('http://localhost', { method: 'POST', body });
return request.headers.get('content-type');
}
console.log('node FormData\nnode Request\n%s\n', contentType(globalThis));
// multipart/form-data; boundary=----formdata-undici-0.7115225130884331
console.log('undici FormData\nundici Request\n%s\n', contentType(undici));
// multipart/form-data; boundary=----formdata-undici-0.7891744173188513
console.log('node FormData\nundici Request\n%s\n', contentType({ FormData, Request: undici.Request }));
// text/plain;charset=UTF-8
console.log('node FormData\nnode-fetch Request\n%s\n', contentType({ FormData, Request: nodeFetch.Request }));
// multipart/form-data; boundary=----9692094296204514736585854209
How come? Once you send FormData it break's?
https://github.com/nodejs/undici/pull/1643
my workaround is to import node-fetch and replace undici in my POST Endpoints
import nodeFetch from 'node-fetch'; const res = await nodeFetch('https://sveltekit-express-bug-tester.herokuapp.com/formdata', { method: 'POST', body: formDataFromFrontend });PROPPER MULITPART CONTENT TYPE ⭐⭐⭐⭐⭐
2022-07-26T11:45:22.308100+00:00 app[web.1]: { 2022-07-26T11:45:22.308112+00:00 app[web.1]: headers: { 2022-07-26T11:45:22.308115+00:00 app[web.1]: host: 'sveltekit-express-bug-tester.herokuapp.com', 2022-07-26T11:45:22.308116+00:00 app[web.1]: connection: 'close', 2022-07-26T11:45:22.308116+00:00 app[web.1]: accept: '*/*', 2022-07-26T11:45:22.308117+00:00 app[web.1]: 'accept-encoding': 'gzip, deflate, br', 2022-07-26T11:45:22.308118+00:00 app[web.1]: 'content-type': 'multipart/form-data; boundary=----3734578923909508881256374353', 2022-07-26T11:45:22.308118+00:00 app[web.1]: 'user-agent': 'node-fetch', 2022-07-26T11:45:22.308119+00:00 app[web.1]: 'x-request-id': '7f3578c7-6963-4098-a193-7bf762561f37', 2022-07-26T11:45:22.308119+00:00 app[web.1]: 'x-forwarded-for': '------------', 2022-07-26T11:45:22.308119+00:00 app[web.1]: 'x-forwarded-proto': 'https', 2022-07-26T11:45:22.308120+00:00 app[web.1]: 'x-forwarded-port': '443', 2022-07-26T11:45:22.308120+00:00 app[web.1]: via: '1.1 vegur', 2022-07-26T11:45:22.308120+00:00 app[web.1]: 'connect-time': '0', 2022-07-26T11:45:22.308120+00:00 app[web.1]: 'x-request-start': '1658835922308', 2022-07-26T11:45:22.308121+00:00 app[web.1]: 'total-route-time': '0', 2022-07-26T11:45:22.308121+00:00 app[web.1]: 'content-length': '218' 2022-07-26T11:45:22.308121+00:00 app[web.1]: }, 2022-07-26T11:45:22.308122+00:00 app[web.1]: file: undefined, 2022-07-26T11:45:22.308122+00:00 app[web.1]: body: [Object: null prototype] { text1: 'Hello', text2: 'World' }
This is weird. This worked for me locally, but does not work on prod (vercel npm run preview), its sent by plain/text with no data. Any1 have any ideas why?