Skip to content

Commit

Permalink
Remove content-type header check for FormData body (#208)
Browse files Browse the repository at this point in the history
  • Loading branch information
rangermeier authored and sindresorhus committed Nov 26, 2019
1 parent 2f37c3f commit 2078a15
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 16 deletions.
7 changes: 1 addition & 6 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ for (const property of globalProperties) {
const isObject = value => value !== null && typeof value === 'object';
const supportsAbortController = typeof globals.AbortController === 'function';
const supportsStreams = typeof globals.ReadableStream === 'function';
const supportsFormData = typeof globals.FormData === 'function';

const deepMerge = (...sources) => {
let returnValue = {};
Expand Down Expand Up @@ -240,11 +239,7 @@ class Ky {
if (this._options.searchParams) {
const url = new URL(this.request.url);
url.search = new URLSearchParams(this._options.searchParams);
this.request = new globals.Request(url, this.request);
}

if (((supportsFormData && this._options.body instanceof globals.FormData) || this._options.body instanceof URLSearchParams) && this.request.headers.has('content-type')) {
throw new Error(`The \`content-type\` header cannot be used with a ${this._options.body.constructor.name} body. It will be set automatically.`);
this.request = new globals.Request(new globals.Request(url, this.request), this._options);
}

if (this._options.json) {
Expand Down
2 changes: 1 addition & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ import ky from 'ky';

### Sending Form Data

Sending form data in Ky is identical to `fetch`. Just pass a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) instance to the `body` option. The `Content-Type` header will be automatically set to `multipart/form-data`. Setting it manually will result in an error.
Sending form data in Ky is identical to `fetch`. Just pass a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) instance to the `body` option. The `Content-Type` header will be automatically set to `multipart/form-data`.

```js
import ky from 'ky';
Expand Down
60 changes: 60 additions & 0 deletions test/browser.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import util from 'util';
import body from 'body';
import {serial as test} from 'ava';
import createTestServer from 'create-test-server';
import withPage from './helpers/with-page';
Expand Down Expand Up @@ -176,3 +178,61 @@ test('throws if does not support ReadableStream', withPage, async (t, page) => {

await server.close();
});

test('FormData with searchParams', withPage, async (t, page) => {
const server = await createTestServer();
server.get('/', (request, response) => {
response.end();
});
server.post('/', async (request, response) => {
const pBody = util.promisify(body);
const requestBody = await pBody(request);

t.regex(requestBody, /bubblegum pie/);
t.deepEqual(request.query, {foo: '1'});

response.end();
});

await page.goto(server.url);
await page.addScriptTag({path: './umd.js'});
await page.evaluate(url => {
window.ky = window.ky.default;
const formData = new window.FormData();
formData.append('file', new window.File(['bubblegum pie'], 'my-file'));
return window.ky(url, {
method: 'post',
searchParams: 'foo=1',
body: formData
});
}, server.url);

await server.close();
});

test('headers are preserved when input is a Request and there are searchParams in the options', withPage, async (t, page) => {
const server = await createTestServer();
server.get('/', (request, response) => {
response.end();
});
server.get('/test', (request, response) => {
t.is(request.headers['content-type'], 'text/css');
t.deepEqual(request.query, {foo: '1'});
response.end();
});

await page.goto(server.url);
await page.addScriptTag({path: './umd.js'});

await page.evaluate(url => {
window.ky = window.ky.default;
const request = new window.Request(url + '/test', {
headers: {'content-type': 'text/css'}
});
return window.ky(request, {
searchParams: 'foo=1'
}).text();
}, server.url);

await server.close();
});
9 changes: 0 additions & 9 deletions test/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -485,15 +485,6 @@ test('throws AbortError when aborted by user', async t => {
await t.throwsAsync(response, {name: 'AbortError'});
});

test('throws when using FormData with `content-type` header', t => {
t.throws(() => ky.post('https://example.com', {
body: new URLSearchParams(),
headers: {
'content-type': ''
}
}), 'The `content-type` header cannot be used with a URLSearchParams body. It will be set automatically.');
});

test('supports Request instance as input', async t => {
const server = await createTestServer();
const inputRequest = new Request(server.url, {method: 'POST'});
Expand Down

0 comments on commit 2078a15

Please sign in to comment.