Skip to content

Commit

Permalink
added headers parameter to append function allowing to set custom h…
Browse files Browse the repository at this point in the history
…eaders for every part of a form data, added custom headers handling for each part in `getParts` function
  • Loading branch information
alicata committed Jun 18, 2024
1 parent 33cdd24 commit 32f6ec1
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 14 deletions.
33 changes: 20 additions & 13 deletions packages/react-native/Libraries/Network/FormData.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
'use strict';

type FormDataValue = string | {name?: string, type?: string, uri: string};
type FormDataNameValuePair = [string, FormDataValue];
type FormDataNameValueTuple = [string, FormDataValue, Headers];

type Headers = {[name: string]: string, ...};
type FormDataPart =
Expand Down Expand Up @@ -44,24 +44,25 @@ type FormDataPart =
* body.append('authToken', 'secret');
* body.append('photo', photo);
* body.append('title', 'A beautiful photo!');
* body.append('someInfo', JSON.stringify({date:new Date()}, { "content-type": "application/json" }));
*
* xhr.open('POST', serverURL);
* xhr.send(body);
*/
class FormData {
_parts: Array<FormDataNameValuePair>;
_parts: Array<FormDataNameValueTuple>;

constructor() {
this._parts = [];
}

append(key: string, value: FormDataValue) {
append(key: string, value: FormDataValue, headers?: Headers) {
// The XMLHttpRequest spec doesn't specify if duplicate keys are allowed.
// MDN says that any new values should be appended to existing values.
// In any case, major browsers allow duplicate keys, so that's what we'll do
// too. They'll simply get appended as additional form data parts in the
// request body, leaving the server to deal with them.
this._parts.push([key, value]);
this._parts.push([key, value, headers]);
}

getAll(key: string): Array<FormDataValue> {
Expand All @@ -71,20 +72,26 @@ class FormData {
}

getParts(): Array<FormDataPart> {
return this._parts.map(([name, value]) => {
const contentDisposition = 'form-data; name="' + name + '"';

const headers: Headers = {'content-disposition': contentDisposition};

return this._parts.map(([name, value, originalHeaders]) => {
if (!originalHeaders) {
originalHeaders = {};
}
const isContentDispositionNeeded =
!originalHeaders['content-disposition'];
const contentDisposition: Partial<Headers> = isContentDispositionNeeded
? {'content-disposition': 'form-data; name="' + name + '"'}
: {};
const headers: Headers = {
...originalHeaders,
...contentDisposition,
};
// The body part is a "blob", which in React Native just means
// an object with a `uri` attribute. Optionally, it can also
// have a `name` and `type` attribute to specify filename and
// content type (cf. web Blob interface.)
if (typeof value === 'object' && !Array.isArray(value) && value) {
if (typeof value.name === 'string') {
headers['content-disposition'] += `; filename="${
value.name
}"; filename*=utf-8''${encodeURI(value.name)}`;
if (typeof value.name === 'string' && isContentDispositionNeeded) {
headers['content-disposition'] += '; filename="' + value.name + '"';
}
if (typeof value.type === 'string') {
headers['content-type'] = value.type;
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/types/modules/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ type FormDataPart =
};

declare class FormData {
append(name: string, value: any): void;
append(name: string, value: any, headers: {[key: string]: string}): void;
getAll(): Array<FormDataValue>;
getParts(): Array<FormDataPart>;
}
Expand Down

0 comments on commit 32f6ec1

Please sign in to comment.