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

[Bug]: CapacitorHttp breaks axios requests with multipart/form-data #7579

Open
2 of 3 tasks
alex-mironov opened this issue Jul 24, 2024 · 10 comments
Open
2 of 3 tasks
Labels
plugin: http type: bug A confirmed bug report

Comments

@alex-mironov
Copy link

Capacitor Version

Latest Dependencies:

  @capacitor/cli: 6.1.1
  @capacitor/core: 6.1.1
  @capacitor/android: 6.1.1
  @capacitor/ios: 6.1.1

Installed Dependencies:

  @capacitor/cli: 6.1.1
  @capacitor/core: 6.1.1
  @capacitor/android: 6.1.1
  @capacitor/ios: 6.1.1

Other API Details

No response

Platforms Affected

  • iOS
  • Android
  • Web

Current Behavior

axios request with Content-Type: "multipart/form-data" is not working. It doesn't send neither Content-Length, Content-Type no content itself.

It happens when CapacitorHttp plugin is enabled.

I have a simple form allowing uploading files.

                <input
                  title="Attach files"
                  type="file"
                  accept="image/*"
                  multiple
                  onChange={handleChange}
                />

and handler

  const handleChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const files = event.target.files;
    const fileFirst = files?.item(0)
    if (!fileFirst) return

    const formData = new FormData();
    formData.append('file', fileFirst);

    const response = await axios.post('/api/upload', formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
      }
    });
    console.log('File uploaded successfully:', response.status);
  };

capacitor.config.ts

import { CapacitorConfig } from "@capacitor/cli";

const config: CapacitorConfig = {
  // ....
  plugins: {
    CapacitorHttp: {
      enabled: true,
    },
    // ...
  },
};

export default config;

Expected Behavior

axios should work no matter if CapacitorHttp enabled or not

Project Reproduction

Additional Information

No response

@alex-mironov
Copy link
Author

@jcesarmobile jcesarmobile added the type: bug A confirmed bug report label Jul 26, 2024
Copy link

ionitron-bot bot commented Jul 26, 2024

This issue has been labeled as type: bug. This label is added to issues that that have been reproduced and are being tracked in our internal issue tracker.

@michaelwolz

This comment was marked as abuse.

@alex-mironov

This comment was marked as off-topic.

@grzegorzCieslik95

This comment was marked as off-topic.

@JuliaBD
Copy link

JuliaBD commented Oct 8, 2024

I have a very similar issue when doing a POST request with formData, using the @angular/common/http HttpClient, and with the CapacitorHttp plugin enabled.

It was working fine (200 results), until I upgraded to Capacitor 6 (tried different versions, all 500 results only), without making any other code changes.

The only change I can see in the request logged by CapacitorHttp is that the headers for Capacitor 5 (= 200 result) include "Content-Type": "multipart/form-data; boundary=--1728408087149", whereas this line is missing for Capacitor 6 (= 500 result).

Capacitor 5 - CapacitorHttp request, with 200 result:

{
    "callbackId": "8641076",
    "pluginId": "CapacitorHttp",
    "methodName": "request",
    "options": {
        "url": "https://loremipsum”,
        "method": "POST",
        "data": [
            {
                "key": "answeredSurvey",
                "value": "{\"id\":511,\"isChecked\":false}",
                "type": "string"
            }
        ],
        "headers": {
            // the only line that differs from breaking request below:
            "Content-Type": "multipart/form-data; boundary=--1728408087149", 
            "Accept": "application/json, text/plain, */*"
        },
        "dataType": "formData"
    }
}

Capacitor 6 - CapacitorHttp request, with 500 result:

{
    "callbackId": "29334309",
    "pluginId": "CapacitorHttp",
    "methodName": "request",
    "options": {
        "url": "https://loremipsum”,
        "method": "POST",
        "data": [
            {
                "key": "answeredSurvey",
                "value": "{\"id\":511,\"isChecked\":false}",
                "type": "string"
            }
        ],
        "headers": {
            "Accept": "application/json, text/plain, */*"
        },
        "dataType": "formData"
    }
}

Here is part of my post function, very simple, no headers specified:

const json = JSON.stringify(answeredSurvey);

const formData = new FormData();
formData.append(‘answeredSurvey’, json);

return this.http.post<PostResponse>(url, formData);

When I include headers in my request with "Content-Type": "multipart/form-data" this does not work, as no boundary is added/extracted.

@davideramoaxa
Copy link

Is it possible that Capacitor 6 removes the Content-Type before executing the fetch?

core-plugin.ts -> buildRequestInit line 424

headers.delete('content-type'); // content-type will be set by `window.fetch` to includy boundary

Besides the Content-Type header is needed (at least in iOS) to set request body:

CapacitorUrlRequest.m line 191

public func setRequestBody(_ body: JSValue, _ dataType: String? = nil) throws {
        let contentType = self.getRequestHeader("Content-Type") as? String

        if contentType != nil {
            request.httpBody = try getRequestData(body, contentType!, dataType)
        }
    }

@davideramoaxa
Copy link

If I remove the check about contentType != nil it works (no special boundary needed in my case, just for test)

@davideramoaxa
Copy link

Further investigations:
Debugging axios code it seems that the lib/helpers/resolveConfig.js
https://github.com/axios/axios/blob/17cab9c2962e6fb1b7342a8b551f944b95554fdf/lib/helpers/resolveConfig.js#L30
clear out the Content-Type.
Not sure if it's correct or not, but for sure something is broken.
The result is that CapacitorHttp receives no Content-Type and this prevents the request.httpBody setting.

Can somebody explain why the browser is expected to set the Content-Type and why this does not happen?

@davideramoaxa
Copy link

axios version 1.x

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
plugin: http type: bug A confirmed bug report
Projects
None yet
Development

No branches or pull requests

6 participants