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

Add support for FormData request body #1835

Merged
merged 15 commits into from Aug 21, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 17 additions & 1 deletion documentation/2-options.md
Expand Up @@ -271,7 +271,7 @@ stream.on('data', console.log);

### `body`

**Type: `string | Buffer | stream.Readable | Generator | AsyncGenerator` or [`form-data` instance](https://github.com/form-data/form-data)**
**Type: `string | Buffer | stream.Readable | Generator | AsyncGenerator | FormData` or [`form-data` instance](https://github.com/form-data/form-data)**

The payload to send.

Expand All @@ -290,6 +290,22 @@ console.log(data);
//=> 'Hello, world!'
```

Since Got 12 you can use spec-compliant FormData objects as request body, such as [`formdata-node`](https://github.com/octet-stream/form-data) or [`formdata-polyfill`](https://github.com/jimmywarting/FormData):
octet-stream marked this conversation as resolved.
Show resolved Hide resolved

```js
import got from 'got';

octet-stream marked this conversation as resolved.
Show resolved Hide resolved
import {FormData} from 'formdata-node'; // or 'formdata-polyfill/esm.min.js'

const form = new FormData();

form.set('greeting', 'Hello, world!');

const {data} = await got.post('https://httpbin.org/post', {
body: form
});
```
octet-stream marked this conversation as resolved.
Show resolved Hide resolved

#### **Note:**
> - If `body` is specified, then the `json` or `form` option cannot be used.

Expand Down
9 changes: 6 additions & 3 deletions source/core/index.ts
Expand Up @@ -128,7 +128,7 @@ const proxiedRequestEvents = [
'upgrade',
] as const;

const noop = () => {};
const noop = (): void => {};

type UrlType = ConstructorParameters<typeof Options>[0];
type OptionsType = ConstructorParameters<typeof Options>[1];
Expand Down Expand Up @@ -572,10 +572,13 @@ export default class Request extends Duplex implements RequestEvents<Request> {

if (isBody) {
// Body is spec-compliant FormData
if (isFormDataLike(options.body) && noContentType) {
if (isFormDataLike(options.body)) {
const encoder = new FormDataEncoder(options.body);

headers['content-type'] = encoder.headers['Content-Type'];
if (noContentType) {
headers['content-type'] = encoder.headers['Content-Type'];
}

headers['content-length'] = encoder.headers['Content-Length'];

options.body = encoder.encode();
Expand Down
15 changes: 15 additions & 0 deletions test/headers.ts
Expand Up @@ -196,6 +196,21 @@ test('sets `content-length` header for spec-compliant FormData', withServer, asy
t.is(headers['content-length'], encoder.headers['Content-Length']);
});

test('manual `content-type` header should be allowed with spec-compliant FormData', withServer, async (t, server, got) => {
server.post('/', echoHeaders);

const form = new FormDataNode();
form.set('a', 'b');
const {body} = await got.post({
headers: {
'content-type': 'custom'
},
body: form,
});
const headers = JSON.parse(body);
t.is(headers['content-type'], 'custom');
});

test('stream as `options.body` does not set `content-length` header', withServer, async (t, server, got) => {
server.post('/', echoHeaders);

Expand Down