developit / redaxios

The Axios API, as an 800 byte Fetch wrapper.
https://npm.im/redaxios
Apache License 2.0
4.72k stars 100 forks source link

POST requests that work with Axios throw CORS error with Redaxios #84

Open Nantris opened 2 years ago

Nantris commented 2 years ago

First, thank you for this awesome project! What a huge transfer size savings Redaxios is over Axios.

We have some code like below that works fine with Axios but fails with Redaxios:

 const response = await axios({
  method: 'POST',
  url: getApiUrl(),
  data: {
    ...formData,
  },
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
  },
});

We get:

Access to fetch at 'https://example.com/' from origin 'http://localhost:1212' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.

It seems possibly related to this question/answer

Nantris commented 2 years ago

@developit any thoughts on this? Is Redaxios supposed to support application/x-www-form-urlencoded content-type?

developit commented 2 years ago

Are you able to show the CORS preflight request and response? It seems like the cors error is masking a failure here relating to the request body encoding.

In your example code, what is the formData value? Is it a plain object, or a FormData instance?

My guess right now is that Axios automatically switches to form-encoding object body values if an x-www-form-encoded content-type is set.

One way to check this would be to see if the following works:

const response = await axios({
  method: 'POST',
  url: getApiUrl(),
  data: new URLSearchParams(formData),
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
  },
});
Nantris commented 2 years ago

Thanks very much for your reply @developit!

In your example code, what is the formData value? Is it a plain object, or a FormData instance?

It is. I tried now with your suggestion but unfortunately it fails in a different way. I no longer see a CORS error, but instead the API endpoint doesn't respond as expected - it returns an HTTP 200 OK status code, but the actual API code fails to process the passed data properly, and returns a failure message.

The Axios payload looks like below - and the failing Redaxios payload (without URLSearchParams) looks the same:

{"endpoint":"ourEndpoint","name":"test","email":"test@test.com","applicationVersion":"x.x.x","issue":"test only"}

The Redaxios payload (with URLSearchParams) looks like this:

endpoint=ourEndpoint&name=test&email=test%40test.com&applicationVersion=x.x.x&issue=test+only

The request headers (minus the User-Agent) for Axios look like this:

:authority: api.example.com
:method: POST
:path: /
:scheme: https
accept: application/json, text/plain, */*
accept-encoding: gzip, deflate, br
accept-language: en-US
cache-control: no-cache
content-length: 134
content-type: application/x-www-form-urlencoded
origin: http://localhost:1111
pragma: no-cache
referer: http://localhost:1111/
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site

For Redaxios (without URLSearchParams) look like this:

content-type: application/json
Referer: http://localhost:1111/

and with URLSearchParams:

:authority: api.example.com
:method: POST
:path: /
:scheme: https
accept: */*
accept-encoding: gzip, deflate, br
accept-language: en-US
cache-control: no-cache
content-length: 114
content-type: application/x-www-form-urlencoded
origin: http://localhost:1111
pragma: no-cache
referer: http://localhost:1111/
sec-fetch-dest: empty
sec-fetch-mode: cors
sec-fetch-site: cross-site

Any clues there? I see differences in the accept header, and also the casing of the referer header, though I doubt that one matters.

Nantris commented 2 years ago

Ah I think I've found a fix, and it's quite simple! It looks like Axios maybe applies JSON.stringify to the object passed as data.

https://programmerlib.com/how-to-send-application-x-www-form-urlencoded-format-data-with-axios-in-vue/

Indeed, wrapping our code to look like this works:

  data: JSON.stringify({
    ...formData,
  }),

Is this logic something you'd be open to integrating into Redaxios to match Axios so we wouldn't need to modify all of our request code?


I suspect the URLSearchParams should probably work too, based on that resource and my other reading - but in my testing, passing an object to the URLSearchParams constructor doesn't result in the data actually being added as an entry. It will only work in my testing (in Firefox 100) if I use URLSearchParams.append(). Not sure what the issue might be there.

Nantris commented 2 years ago

Friendly bump. @developit what do you think? Could this be integrated directly into redaxios?

Nantris commented 2 years ago

Friendly bump @developit, whenever you have a chance. I think this would be a valuable addition to Redaxios.

Nantris commented 2 years ago

Friendly bump.

developit commented 1 year ago

Hey, sorry for the inactivity on this. I haven't had time to make the change because it requires a fair amount of byte-shaving to offset (the string "application/x-www-form-encoded" is very long). I'm hoping I can come up with a clever solution (eg: browser sending the header by using FormData to encode?), but haven't found one yet.

The affected lines are these ones: https://github.com/developit/redaxios/blob/ad40de9175109bbe144fd2ab81a001132f437184/src/index.js#L174-L175

Nantris commented 1 year ago

Thanks for the update @developit!