Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot Make API call to http endpoint when using formData[Tried with both fetch and axios] #44737

Open
H3tansh opened this issue May 31, 2024 · 17 comments · May be fixed by #46543
Open

Cannot Make API call to http endpoint when using formData[Tried with both fetch and axios] #44737

H3tansh opened this issue May 31, 2024 · 17 comments · May be fixed by #46543
Labels
Needs: Repro This issue could be improved with a clear list of steps to reproduce the issue. Needs: Triage 🔍 🌐Networking Related to a networking API. Platform: iOS iOS applications.

Comments

@H3tansh
Copy link

H3tansh commented May 31, 2024

Description

Cannot Make API call to http endpoint when using formData[Tried with both fetch and axios]

I've tried addring clearTextTrafic true in android Mainfest.xml file

Working fine in iOS, and working well with normal APIs ( without formData), only API with formData having this issue.

Steps to reproduce

const formData = new FormData()
formData.append('audio_file', {
name: fileName,
type: 'audio/mp3',
uri: filePath
})

await fetch('http endpoint', {
    body: formData,
    method: 'POST',
    headers: {
        'Content-Type': 'multipart/form-data',
        Authorization:
            'Bearer token'
    }
})
    .then(res => {
        console.log('test result ', res)
        console.log('test result status ', res.status)
    })
    .catch(e => {
        console.log('test e ', e)
    })

This returns network error

React Native Version

0.74.1

Affected Platforms

Runtime - Android

Output of npx react-native info

REACT-NATIVE-INFO output:-

System:
OS: macOS 14.2.1
CPU: (8) arm64 Apple M1
Memory: 599.88 MB / 16.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 18.19.0
path: ~/.nvm/versions/node/v18.19.0/bin/node
Yarn:
version: 3.6.4
path: /opt/homebrew/bin/yarn
npm:
version: 10.2.3
path: ~/.nvm/versions/node/v18.19.0/bin/npm
Watchman:
version: 2024.04.15.00
path: /opt/homebrew/bin/watchman
Managers:
CocoaPods:
version: 1.15.2
path: /opt/homebrew/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 23.0
- iOS 17.0
- macOS 14.0
- tvOS 17.0
- watchOS 10.0
Android SDK:
API Levels:
- "27"
- "28"
- "29"
- "30"
- "31"
- "32"
- "33"
- "34"
Build Tools:
- 27.0.3
- 28.0.3
- 29.0.2
- 29.0.3
- 30.0.2
- 30.0.3
- 31.0.0
- 32.0.0
- 32.1.0
- 33.0.0
- 33.0.0
- 33.0.1
- 34.0.0
System Images:
- android-32 | Google APIs ARM 64 v8a
- android-33 | Google APIs ARM 64 v8a
Android NDK: Not Found
IDEs:
Android Studio: 2023.1 AI-231.9392.1.2311.11330709
Xcode:
version: 15.0/15A240d
path: /usr/bin/xcodebuild
Languages:
Java:
version: 21.0.2
path: /usr/bin/javac
Ruby:
version: 2.6.10
path: /usr/bin/ruby
npmPackages:
"@react-native-community/cli": Not Found
react:
installed: 18.2.0
wanted: 18.2.0
react-native:
installed: 0.74.1
wanted: 0.74.1
react-native-macos: Not Found
npmGlobalPackages:
"react-native": Not Found
Android:
hermesEnabled: true
newArchEnabled: false
iOS:
hermesEnabled: true
newArchEnabled: false

Stacktrace or Logs

Instance Create Error [AxiosError: Network Error]

Reproducer

private repo cannot provide link

Screenshots and Videos

No response

Copy link

⚠️ Missing Reproducible Example
ℹ️ We could not detect a reproducible example in your issue report. Please provide either:
  • If your bug is UI related: a Snack
  • If your bug is build/update related: use our Reproducer Template. A reproducer needs to be in a GitHub repository under your username.

@github-actions github-actions bot added Needs: Author Feedback Needs: Repro This issue could be improved with a clear list of steps to reproduce the issue. Platform: iOS iOS applications. 🌐Networking Related to a networking API. labels May 31, 2024
@Ahmad-Elsayed
Copy link

Hello,

I've come across your issue and would like to suggest a couple of potential solutions that might resolve the problem you're experiencing with FormData on Android.

Firstly, ensure that the URI for the file you're appending to FormData has the correct scheme. On Android, the file URI should start with file://. Additionally, verify that the MIME type is accurate. For audio files, instead of 'audio/mp3', you should use 'audio/mpeg'.

Here's an updated snippet of your code with these changes:

const formData = new FormData();
formData.append('audio_file', {
  name: fileName,
  type: 'audio/mpeg', // Correct MIME type
  uri: `file://${filePath}` // Correct URI scheme
});

try {
  const response = await fetch('http endpoint', {
    body: formData,
    method: 'POST',
    headers: {
      'Content-Type': 'multipart/form-data',
      Authorization: 'Bearer token'
    }
  });
  console.log('test result ', response);
  console.log('test result status ', response.status);
} catch (e) {
  console.error('test error ', e);
}

@H3tansh
Copy link
Author

H3tansh commented Jun 3, 2024

accurate

Tried everything including your suggestion, still no luck 😓

@github-actions github-actions bot added Needs: Attention Issues where the author has responded to feedback. and removed Needs: Author Feedback labels Jun 3, 2024
@cortinico cortinico removed the Needs: Attention Issues where the author has responded to feedback. label Jun 4, 2024
@chr4ss12
Copy link

I don't know if this helps OR is related, but we have been using FormData for 4+ years, and it broke when upgrading to 0.74.3 from 0.72, the culprit being

addition of

headers['content-disposition'] += `; filename="${
            value.name
          }"; filename*=utf-8''${encodeURI(value.name)}`;

specifically filename* part, so I've had to patch-package and revert it for time being.

I kept getting "request is malformed". YOu can debug network errors a bit better using Charles and SSL stripping, so you will see exactly what is being sent/received as well the format etc.

7c7e9e6

@robertying

@robertying
Copy link
Contributor

@chr4ss12 Not sure why you mentioned me. This change of filename* added support for non-ascii filenames, and it's suggested to keep both filename= and filename*= in the header value for maximum compatibility. As the MDN doc says, "when both filename and filename* are present in a single header field value, filename* is preferred over filename when both are understood."

Check the encoded name. If the filename*= part is correctly encoded, but you still get errors from the request, it might be an issue with the HTTP server that's handling the header.

@chr4ss12
Copy link

@robertying I saw you created the commit (sorry if that's not the case - and you only approved it). I am using very simple code to upload files to google cloud storage, and it stopped working after that commit, there's nothing I can do with the HTTP server that's handling the header (the name= is always hardcoded to "myfile.jpg"),

just giving heads up to anyone else experiencing this issue.

@robertying
Copy link
Contributor

@chr4ss12 I did make the change. This change was necessary to support utf-8 filenames.

If you could have a repro or try constructing the same header value in a form data request in Postman to isolate it, and see if it fixes the issue, it would be easier to see where it went wrong. I previously tested the multipart upload with Express.js and Multer, they received files fine.

@chr4ss12
Copy link

@robertying I've checked the code, the spec, played around the request in Charles, and everything checks out - as far as I can tell it looks all right.

The problem is with google Cloud storage not knowing how to read the filename*=UTF-8''dummy.jpg part of the request - I will try and see if I can raise an issue in google tracker.

In meanwhile, am not too convinced of this change though, mainly because the way they implemented the syntax in the first place:

filename*=utf-8''

I. mean really? did they not want to throw in anything else that for sure will break ANY implementation that has no filename*= parsing support, unless that was their idea to begin with...

Also the app I use 'Charles' which I use for debugging could not parse the multipart data because it thought it is malformed (it is not), it seems this filename*= syntax is not that well adapted, perhaps something more along side of lines

if (name contains non ascii characters) //emit  the filename*=

@robertying
Copy link
Contributor

@chr4ss12 Yes in theory if the parser doesn't know the keys in the header value, it should throw them away, according to the doc. In practice though, I can definitely see implementations that don't conform to the spec.

Feel free to file a bug and propose a fix in a PR.

Thank you for investigating this. 🙏

@Tadimsky
Copy link

Tadimsky commented Aug 12, 2024

We experienced this exact same issue due to this bug - it looks like it's either that we're not encoding the name correctly or the server cannot parse this value.
In our case, our server is Go-based and will not parse the filename provided if there are any parentheses in the filename.

This is an example of a header that we're getting from React Native now, which is not being parsed correctly by Go.

form-data; name="1"; filename="84fb1493-321f-471f-9c63-7e98019b2931 (1).pdf"; filename*=utf-8''84fb1493-321f-471f-9c63-7e98019b2931 (1).pdf

This is the go stdlib for mime parsing:
https://cs.opensource.google/go/go/+/refs/tags/go1.22.6:src/mime/mediatype.go;l=161-169

Do we know if encodeURI is valid per the RFC to encode a filename in utf-8?
7c7e9e6#diff-756cfe2421bc80e4c12e447a744cff5190da329b1a59614a7ebd853873f6a741R87

Should we not be using encodeURIComponent instead?

Compared to encodeURI(), this function encodes more characters, including those that are part of the URI syntax.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent

@robertying
Copy link
Contributor

@Tadimsky encodeURIComponent doesn't encode parentheses () either.

Maybe check if the server can parse the header if you get rid of the filename*= part.

@Tadimsky
Copy link

Ah you're right, sorry about that!

It looks like MDN has an example of how to create RFC-5987 valid values for Content-Disposition which is a combination of encodeURIComponent and some custom replacements.

I think we should probably be using something like that in React Native?

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#encoding_for_content-disposition_and_link_headers

@robertying
Copy link
Contributor

@Tadimsky Thanks so much for finding that. Would you like to raise a PR with that?

@Tadimsky
Copy link

Yes, let me give that a try!

@foyarash
Copy link

Hello,

I just encountered the same problem with the Content-Disposition, and came onto saw this issue. After some digging into the MDN docs, i found that this section states that the filename* should not be there for a Content-Disposition thats related to a form-data.

Note that the request header does not have the filename* parameter and does not allow RFC 5987 encoding.

I hope I'm understanding that correctly. I can create a PR for this if necessary.

@foyarash foyarash linked a pull request Sep 17, 2024 that will close this issue
@Igornorlin
Copy link

Hello,

I just encountered the same problem with the Content-Disposition, and came onto saw this issue. After some digging into the MDN docs, i found that this section states that the filename* should not be there for a Content-Disposition thats related to a form-data.

Note that the request header does not have the filename* parameter and does not allow RFC 5987 encoding.

I hope I'm understanding that correctly. I can create a PR for this if necessary.

My understanding is exactly that as well.

This issue has caused unexpected issues for us too.

@m4a1carbin4
Copy link

Hello,
I Think i'm having the same problem here.

async postMultipart(url: string, data: any) {
        const formData = new FormData();
        let imageUrl: string = '';

        Object.keys(data).map(key => {
            console.log(key);
            const value = data[key];

            if (key == 'tags' || key == 'image') {
                console.log('tags value will be ignored');
            } else if (key == 'imageString') {
                imageUrl = value;
            } else if (key == 'aiGenerated') {
                formData.append('ai_generated', value);
            } else if (Array.isArray(value)) {
                value.forEach(item => formData.append(`${key}`, item));
            } else {
                if (value === '') {
                    formData.append(key, 'none');
                } else {
                    formData.append(key, value);
                }
            }

            return true;
        });

        const imageBlob = await loadImageFile(imageUrl);
        const extension = imageBlob.type.split('/')[1];
        const fileName = imageUrl.split('/').pop();
        console.log('value');
        const file = await new File([imageBlob], fileName + '.' + extension);
        console.log(file);
        await formData.append('image', file, fileName + '.' + extension);
        /*await formData.append('image', {
            uri: imageUrl,
            name: file.name,
            type: file.type,
        });*/
        console.log('formData', formData);

        const token = await getPersistData(DataPersistKeys.TOKEN);

        const response = await fetch(this.config.url + url, {
            method: 'POST',
            headers: {
                'Content-Type': 'multipart/form-data',
                Authorization: `Bearer ${token}`,
            },
            body: formData,
        });

        /*await this.axiosInstance.post(url, formData, {
            headers: {
                'Content-Type': 'multipart/form-data',
            },
            transformRequest: (data, headers) => {
                return data;
            },
        }); */

        return response;
    }

Even when using the code that has been commented out, a network error keep occurs. (both fetch and axios)
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs: Repro This issue could be improved with a clear list of steps to reproduce the issue. Needs: Triage 🔍 🌐Networking Related to a networking API. Platform: iOS iOS applications.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants