mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(fetch): smarter JSON.stringify for application/json requests (#10245)
This commit is contained in:
parent
e3ba3eab11
commit
fbb3c88f3c
@ -142,14 +142,18 @@ export class APIRequestContext extends ChannelOwner<channels.APIRequestContextCh
|
|||||||
let multipartData: channels.FormField[] | undefined;
|
let multipartData: channels.FormField[] | undefined;
|
||||||
let postDataBuffer: Buffer | undefined;
|
let postDataBuffer: Buffer | undefined;
|
||||||
if (options.data !== undefined) {
|
if (options.data !== undefined) {
|
||||||
if (isString(options.data))
|
if (isString(options.data)) {
|
||||||
postDataBuffer = Buffer.from(options.data, 'utf8');
|
if (isJsonContentType(headers))
|
||||||
else if (Buffer.isBuffer(options.data))
|
jsonData = options.data;
|
||||||
|
else
|
||||||
|
postDataBuffer = Buffer.from(options.data, 'utf8');
|
||||||
|
} else if (Buffer.isBuffer(options.data)) {
|
||||||
postDataBuffer = options.data;
|
postDataBuffer = options.data;
|
||||||
else if (typeof options.data === 'object')
|
} else if (typeof options.data === 'object' || typeof options.data === 'number' || typeof options.data === 'boolean') {
|
||||||
jsonData = options.data;
|
jsonData = options.data;
|
||||||
else
|
} else {
|
||||||
throw new Error(`Unexpected 'data' type`);
|
throw new Error(`Unexpected 'data' type`);
|
||||||
|
}
|
||||||
} else if (options.form) {
|
} else if (options.form) {
|
||||||
formData = objectToArray(options.form);
|
formData = objectToArray(options.form);
|
||||||
} else if (options.multipart) {
|
} else if (options.multipart) {
|
||||||
@ -301,3 +305,13 @@ async function readStreamToJson(stream: fs.ReadStream): Promise<ServerFilePayloa
|
|||||||
buffer: buffer.toString('base64'),
|
buffer: buffer.toString('base64'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isJsonContentType(headers?: HeadersArray): boolean {
|
||||||
|
if (!headers)
|
||||||
|
return false;
|
||||||
|
for (const { name, value } of headers) {
|
||||||
|
if (name.toLocaleLowerCase() === 'content-type')
|
||||||
|
return value === 'application/json';
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@ -488,10 +488,24 @@ function parseCookie(header: string): types.NetworkCookie | null {
|
|||||||
return cookie;
|
return cookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isJsonParsable(value: any) {
|
||||||
|
if (typeof value !== 'string')
|
||||||
|
return false;
|
||||||
|
try {
|
||||||
|
JSON.parse(value);
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof SyntaxError)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function serializePostData(params: channels.APIRequestContextFetchParams, headers: { [name: string]: string }): Buffer | undefined {
|
function serializePostData(params: channels.APIRequestContextFetchParams, headers: { [name: string]: string }): Buffer | undefined {
|
||||||
assert((params.postData ? 1 : 0) + (params.jsonData ? 1 : 0) + (params.formData ? 1 : 0) + (params.multipartData ? 1 : 0) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`);
|
assert((params.postData ? 1 : 0) + (params.jsonData ? 1 : 0) + (params.formData ? 1 : 0) + (params.multipartData ? 1 : 0) <= 1, `Only one of 'data', 'form' or 'multipart' can be specified`);
|
||||||
if (params.jsonData) {
|
if (params.jsonData) {
|
||||||
const json = JSON.stringify(params.jsonData);
|
const json = isJsonParsable(params.jsonData) ? params.jsonData : JSON.stringify(params.jsonData);
|
||||||
headers['content-type'] ??= 'application/json';
|
headers['content-type'] ??= 'application/json';
|
||||||
return Buffer.from(json, 'utf8');
|
return Buffer.from(json, 'utf8');
|
||||||
} else if (params.formData) {
|
} else if (params.formData) {
|
||||||
|
|||||||
@ -234,3 +234,63 @@ it('should remove content-length from reidrected post requests', async ({ playwr
|
|||||||
expect(req2.headers['content-length']).toBe(undefined);
|
expect(req2.headers['content-length']).toBe(undefined);
|
||||||
await request.dispose();
|
await request.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const serialization = [
|
||||||
|
['object', { 'foo': 'bar' }],
|
||||||
|
['array', ['foo', 'bar', 2021]],
|
||||||
|
['string', 'foo'],
|
||||||
|
['bool', true],
|
||||||
|
['number', 2021],
|
||||||
|
];
|
||||||
|
for (const [type, value] of serialization) {
|
||||||
|
const stringifiedValue = JSON.stringify(value);
|
||||||
|
it(`should json stringify ${type} body when content-type is application/json`, async ({ playwright, server }) => {
|
||||||
|
const request = await playwright.request.newContext();
|
||||||
|
const [req] = await Promise.all([
|
||||||
|
server.waitForRequest('/empty.html'),
|
||||||
|
request.post(server.EMPTY_PAGE, {
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
},
|
||||||
|
data: value
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
const body = await req.postBody;
|
||||||
|
expect(body.toString()).toEqual(stringifiedValue);
|
||||||
|
await request.dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should not double stringify ${type} body when content-type is application/json`, async ({ playwright, server }) => {
|
||||||
|
const request = await playwright.request.newContext();
|
||||||
|
const [req] = await Promise.all([
|
||||||
|
server.waitForRequest('/empty.html'),
|
||||||
|
request.post(server.EMPTY_PAGE, {
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
},
|
||||||
|
data: stringifiedValue
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
const body = await req.postBody;
|
||||||
|
expect(body.toString()).toEqual(stringifiedValue);
|
||||||
|
await request.dispose();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it(`should accept already serialized data as Buffer when content-type is application/json`, async ({ playwright, server }) => {
|
||||||
|
const request = await playwright.request.newContext();
|
||||||
|
const value = JSON.stringify(JSON.stringify({ 'foo': 'bar' }));
|
||||||
|
const [req] = await Promise.all([
|
||||||
|
server.waitForRequest('/empty.html'),
|
||||||
|
request.post(server.EMPTY_PAGE, {
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json'
|
||||||
|
},
|
||||||
|
data: Buffer.from(value, 'utf8')
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
const body = await req.postBody;
|
||||||
|
expect(body.toString()).toEqual(value);
|
||||||
|
await request.dispose();
|
||||||
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user