diff --git a/docs/src/api/class-request.md b/docs/src/api/class-request.md index 46a5e95d2531f..e13d9de69f32f 100644 --- a/docs/src/api/class-request.md +++ b/docs/src/api/class-request.md @@ -23,32 +23,6 @@ request is issued to a redirected url. An object with all the request HTTP headers associated with this request. The header names are lower-cased. -## async method: Request.body -* since: v1.57 -- returns: <[null]|[string]> - -The request body, if present. - -## async method: Request.bodyBuffer -* since: v1.57 -- returns: <[null]|[Buffer]> - -The request body in a binary form. Returns null if the request has no body. - -## async method: Request.bodyJSON -* since: v1.57 -* langs: js, python -- returns: <[null]|[Serializable]> - -Returns the request body as a parsed JSON object. If the request `Content-Type` is `application/x-www-form-urlencoded`, this method returns a key/value object parsed from the form data. Otherwise, it parses the body as JSON. - -## async method: Request.bodyJSON -* since: v1.57 -* langs: csharp -- returns: <[null]|[JsonElement]> - -Returns the request body as a parsed JSON object. If the request `Content-Type` is `application/x-www-form-urlencoded`, this method returns a key/value object parsed from the form data. Otherwise, it parses the body as JSON. - ## method: Request.failure * since: v1.8 - returns: <[null]|[string]> @@ -175,33 +149,35 @@ Request's method (GET, POST, etc.) ## method: Request.postData * since: v1.8 -* discouraged: Use [`method: Request.body`] instead. - returns: <[null]|[string]> -The request body, if present. +Request's post body, if any. ## method: Request.postDataBuffer * since: v1.8 -* discouraged: Use [`method: Request.bodyBuffer`] instead. - returns: <[null]|[Buffer]> -The request body in a binary form. Returns null if the request has no body. +Request's post body in a binary form, if any. ## method: Request.postDataJSON * since: v1.8 * langs: js, python -* discouraged: Use [`method: Request.bodyJSON`] instead. - returns: <[null]|[Serializable]> -Returns the request body as a parsed JSON object. If the request `Content-Type` is `application/x-www-form-urlencoded`, this method returns a key/value object parsed from the form data. Otherwise, it parses the body as JSON. +Returns parsed request's body for `form-urlencoded` and JSON as a fallback if any. + +When the response is `application/x-www-form-urlencoded` then a key/value object of the values will be returned. +Otherwise it will be parsed as JSON. ## method: Request.postDataJSON * since: v1.12 * langs: csharp -* discouraged: Use [`method: Request.bodyJSON`] instead. - returns: <[null]|[JsonElement]> -Returns the request body as a parsed JSON object. If the request `Content-Type` is `application/x-www-form-urlencoded`, this method returns a key/value object parsed from the form data. Otherwise, it parses the body as JSON. +Returns parsed request's body for `form-urlencoded` and JSON as a fallback if any. + +When the response is `application/x-www-form-urlencoded` then a key/value object of the values will be returned. +Otherwise it will be parsed as JSON. ## method: Request.redirectedFrom * since: v1.8 diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 55fb65559defd..5e9b254130457 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -20378,23 +20378,6 @@ export interface Request { */ allHeaders(): Promise<{ [key: string]: string; }>; - /** - * The request body, if present. - */ - body(): Promise; - - /** - * The request body in a binary form. Returns null if the request has no body. - */ - bodyBuffer(): Promise; - - /** - * Returns the request body as a parsed JSON object. If the request `Content-Type` is - * `application/x-www-form-urlencoded`, this method returns a key/value object parsed from the form data. Otherwise, - * it parses the body as JSON. - */ - bodyJSON(): Promise; - /** * The method returns `null` unless this request has failed, as reported by `requestfailed` event. * @@ -20492,25 +20475,20 @@ export interface Request { method(): string; /** - * **NOTE** Use [request.body()](https://playwright.dev/docs/api/class-request#request-body) instead. - * - * The request body, if present. + * Request's post body, if any. */ postData(): null|string; /** - * **NOTE** Use [request.bodyBuffer()](https://playwright.dev/docs/api/class-request#request-body-buffer) instead. - * - * The request body in a binary form. Returns null if the request has no body. + * Request's post body in a binary form, if any. */ postDataBuffer(): null|Buffer; /** - * **NOTE** Use [request.bodyJSON()](https://playwright.dev/docs/api/class-request#request-body-json) instead. + * Returns parsed request's body for `form-urlencoded` and JSON as a fallback if any. * - * Returns the request body as a parsed JSON object. If the request `Content-Type` is - * `application/x-www-form-urlencoded`, this method returns a key/value object parsed from the form data. Otherwise, - * it parses the body as JSON. + * When the response is `application/x-www-form-urlencoded` then a key/value object of the values will be returned. + * Otherwise it will be parsed as JSON. */ postDataJSON(): null|Serializable; diff --git a/packages/playwright-core/src/client/network.ts b/packages/playwright-core/src/client/network.ts index 4f74b462a8940..727090961ad25 100644 --- a/packages/playwright-core/src/client/network.ts +++ b/packages/playwright-core/src/client/network.ts @@ -134,18 +134,6 @@ export class Request extends ChannelOwner implements ap return this._fallbackOverrides.method || this._initializer.method; } - async body(): Promise { - return (this._fallbackOverrides.postDataBuffer || (await this._channel.body()).body)?.toString('utf-8') || null; - } - - async bodyBuffer(): Promise { - return this._fallbackOverrides.postDataBuffer || (await this._channel.body()).body || null; - } - - async bodyJSON(): Promise { - return this._postDataJSON(await this.body()); - } - postData(): string | null { return (this._fallbackOverrides.postDataBuffer || this._initializer.postData)?.toString('utf-8') || null; } @@ -156,10 +144,6 @@ export class Request extends ChannelOwner implements ap postDataJSON(): Object | null { const postData = this.postData(); - return this._postDataJSON(postData); - } - - private _postDataJSON(postData: string | null): Object | null { if (!postData) return null; diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index c2f86058291d6..18980d8e18b46 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -2231,10 +2231,6 @@ scheme.RequestInitializer = tObject({ hasResponse: tBoolean, }); scheme.RequestResponseEvent = tOptional(tObject({})); -scheme.RequestBodyParams = tOptional(tObject({})); -scheme.RequestBodyResult = tObject({ - body: tOptional(tBinary), -}); scheme.RequestResponseParams = tOptional(tObject({})); scheme.RequestResponseResult = tObject({ response: tOptional(tChannel(['Response'])), diff --git a/packages/playwright-core/src/server/dispatchers/networkDispatchers.ts b/packages/playwright-core/src/server/dispatchers/networkDispatchers.ts index 363c3f7b6575b..286a11ae575f8 100644 --- a/packages/playwright-core/src/server/dispatchers/networkDispatchers.ts +++ b/packages/playwright-core/src/server/dispatchers/networkDispatchers.ts @@ -66,11 +66,6 @@ export class RequestDispatcher extends Dispatcher this._dispatchEvent('response', {})); } - async body(params: channels.RequestBodyParams, progress: Progress): Promise { - const postData = this._object.postDataBuffer(); - return { body: postData === null ? undefined : postData }; - } - async rawRequestHeaders(params: channels.RequestRawRequestHeadersParams, progress: Progress): Promise { return { headers: await progress.race(this._object.rawRequestHeaders()) }; } diff --git a/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts b/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts index 3b85ae2eea60e..a190a367d3867 100644 --- a/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts +++ b/packages/playwright-core/src/utils/isomorphic/protocolMetainfo.ts @@ -236,7 +236,6 @@ export const methodMetainfo = new Map; - /** - * The request body, if present. - */ - body(): Promise; - - /** - * The request body in a binary form. Returns null if the request has no body. - */ - bodyBuffer(): Promise; - - /** - * Returns the request body as a parsed JSON object. If the request `Content-Type` is - * `application/x-www-form-urlencoded`, this method returns a key/value object parsed from the form data. Otherwise, - * it parses the body as JSON. - */ - bodyJSON(): Promise; - /** * The method returns `null` unless this request has failed, as reported by `requestfailed` event. * @@ -20492,25 +20475,20 @@ export interface Request { method(): string; /** - * **NOTE** Use [request.body()](https://playwright.dev/docs/api/class-request#request-body) instead. - * - * The request body, if present. + * Request's post body, if any. */ postData(): null|string; /** - * **NOTE** Use [request.bodyBuffer()](https://playwright.dev/docs/api/class-request#request-body-buffer) instead. - * - * The request body in a binary form. Returns null if the request has no body. + * Request's post body in a binary form, if any. */ postDataBuffer(): null|Buffer; /** - * **NOTE** Use [request.bodyJSON()](https://playwright.dev/docs/api/class-request#request-body-json) instead. + * Returns parsed request's body for `form-urlencoded` and JSON as a fallback if any. * - * Returns the request body as a parsed JSON object. If the request `Content-Type` is - * `application/x-www-form-urlencoded`, this method returns a key/value object parsed from the form data. Otherwise, - * it parses the body as JSON. + * When the response is `application/x-www-form-urlencoded` then a key/value object of the values will be returned. + * Otherwise it will be parsed as JSON. */ postDataJSON(): null|Serializable; diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index d001a36af44c6..8a2e3db8acef4 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -3829,16 +3829,10 @@ export interface RequestEventTarget { } export interface RequestChannel extends RequestEventTarget, Channel { _type_Request: boolean; - body(params?: RequestBodyParams, progress?: Progress): Promise; response(params?: RequestResponseParams, progress?: Progress): Promise; rawRequestHeaders(params?: RequestRawRequestHeadersParams, progress?: Progress): Promise; } export type RequestResponseEvent = {}; -export type RequestBodyParams = {}; -export type RequestBodyOptions = {}; -export type RequestBodyResult = { - body?: Binary, -}; export type RequestResponseParams = {}; export type RequestResponseOptions = {}; export type RequestResponseResult = { diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 1629cf5fede2d..2f535bf6448e9 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -3391,12 +3391,6 @@ Request: commands: - body: - title: Get request body - group: getter - returns: - body: binary? - response: internal: true returns: diff --git a/tests/page/network-request-body.spec.ts b/tests/page/network-request-body.spec.ts deleted file mode 100644 index 46585838fd974..0000000000000 --- a/tests/page/network-request-body.spec.ts +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { test as it, expect } from './pageTest'; - -it('should return correct request body buffer for utf-8 body', async ({ page, server }) => { - await page.goto(server.EMPTY_PAGE); - const value = 'baẞ'; - const [request] = await Promise.all([ - page.waitForRequest('**'), - page.evaluate(({ url, value }) => { - const request = new Request(url, { - method: 'POST', - body: JSON.stringify(value), - }); - request.headers.set('content-type', 'application/json;charset=UTF-8'); - return fetch(request); - }, { url: server.PREFIX + '/title.html', value }) - ]); - expect((await request.bodyBuffer()).equals(Buffer.from(JSON.stringify(value), 'utf-8'))).toBe(true); - expect(await request.bodyJSON()).toBe(value); -}); - -it('should return request body w/o content-type @smoke', async ({ page, server }) => { - await page.goto(server.EMPTY_PAGE); - const [request] = await Promise.all([ - page.waitForRequest('**'), - page.evaluate(({ url }) => { - const request = new Request(url, { - method: 'POST', - body: JSON.stringify({ value: 42 }), - }); - request.headers.set('content-type', ''); - return fetch(request); - }, { url: server.PREFIX + '/title.html' }) - ]); - expect(await request.bodyJSON()).toEqual({ value: 42 }); -}); - -it('should throw on invalid JSON in post data', async ({ page, server }) => { - await page.goto(server.EMPTY_PAGE); - const [request] = await Promise.all([ - page.waitForRequest('**'), - page.evaluate(({ url }) => { - const request = new Request(url, { - method: 'POST', - body: '', - }); - return fetch(request); - }, { url: server.PREFIX + '/title.html' }) - ]); - let error; - try { - await request.bodyJSON(); - } catch (e) { - error = e; - } - expect(error.message).toContain('POST data is not a valid JSON object: '); -}); - -it('should return body for PUT requests', async ({ page, server }) => { - await page.goto(server.EMPTY_PAGE); - const [request] = await Promise.all([ - page.waitForRequest('**'), - page.evaluate(({ url }) => { - const request = new Request(url, { - method: 'PUT', - body: JSON.stringify({ value: 42 }), - }); - return fetch(request); - }, { url: server.PREFIX + '/title.html' }) - ]); - expect(await request.bodyJSON()).toEqual({ value: 42 }); -}); - -it('should get request body for file/blob', async ({ page, server, browserName }) => { - it.fail(browserName === 'webkit' || browserName === 'chromium'); - await page.goto(server.EMPTY_PAGE); - const [request] = await Promise.all([ - page.waitForRequest('**/*'), - page.evaluate(() => { - const file = new File(['file-contents'], 'filename.txt'); - - void fetch('/data', { - method: 'POST', - headers: { - 'content-type': 'application/octet-stream' - }, - body: file - }); - }) - ]); - expect(await request.body()).toBe('file-contents'); -}); - -it('should get request body for navigator.sendBeacon api calls', async ({ page, server, browserName }) => { - it.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/12231' }); - it.fail(browserName === 'chromium', 'body is empty'); - it.fail(browserName === 'webkit', 'body is empty'); - await page.goto(server.EMPTY_PAGE); - const [request] = await Promise.all([ - page.waitForRequest('**/*'), - page.evaluate(() => navigator.sendBeacon(window.location.origin + '/api/foo', new Blob([JSON.stringify({ foo: 'bar' })]))) - ]); - expect(request.method()).toBe('POST'); - expect(request.url()).toBe(server.PREFIX + '/api/foo'); - expect(await request.bodyJSON()).toStrictEqual({ foo: 'bar' }); -}); diff --git a/utils/generate_channels.js b/utils/generate_channels.js index 07865ba3c04ad..9466dd843f7a2 100755 --- a/utils/generate_channels.js +++ b/utils/generate_channels.js @@ -297,7 +297,7 @@ for (const [name, item] of Object.entries(protocol)) { if (!method.title && !method.internal) throw new Error(`Method "${className}.${methodName}" must have a "title" because it is not "internal" in protocol.yml`); if (method.group && method.internal) - throw new Error(`Method "${className}.${methodName}" must not specify "group" because it is "internal" in protocol.yml`); + throw new Error(`Method "${className}.${methodName}" must should not specify "group" because it is "internal" in protocol.yml`); if (method.group && !['getter', 'configuration', 'route', 'default'].includes(method.group)) throw new Error(`Unknown group "${method.group}" for method "${className}.${methodName}" in protocol.yml`); const internalProp = method.internal ? ` internal: ${method.internal},` : '';