mirror of
https://github.com/microsoft/playwright.git
synced 2024-12-14 05:37:20 +03: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 postDataBuffer: Buffer | undefined;
|
||||
if (options.data !== undefined) {
|
||||
if (isString(options.data))
|
||||
postDataBuffer = Buffer.from(options.data, 'utf8');
|
||||
else if (Buffer.isBuffer(options.data))
|
||||
if (isString(options.data)) {
|
||||
if (isJsonContentType(headers))
|
||||
jsonData = options.data;
|
||||
else
|
||||
postDataBuffer = Buffer.from(options.data, 'utf8');
|
||||
} else if (Buffer.isBuffer(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;
|
||||
else
|
||||
} else {
|
||||
throw new Error(`Unexpected 'data' type`);
|
||||
}
|
||||
} else if (options.form) {
|
||||
formData = objectToArray(options.form);
|
||||
} else if (options.multipart) {
|
||||
@ -300,4 +304,14 @@ async function readStreamToJson(stream: fs.ReadStream): Promise<ServerFilePayloa
|
||||
mimeType: mime.getType(streamPath) || 'application/octet-stream',
|
||||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
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) {
|
||||
const json = JSON.stringify(params.jsonData);
|
||||
const json = isJsonParsable(params.jsonData) ? params.jsonData : JSON.stringify(params.jsonData);
|
||||
headers['content-type'] ??= 'application/json';
|
||||
return Buffer.from(json, 'utf8');
|
||||
} 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);
|
||||
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…
Reference in New Issue
Block a user