Skip to content

Commit 1cc6bbb

Browse files
authoredOct 31, 2022
Parse empty response bodies without throwing when using .json() (#459)
1 parent dddf7ba commit 1cc6bbb

File tree

4 files changed

+26
-7
lines changed

4 files changed

+26
-7
lines changed
 

‎readme.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ The `input` and `options` are the same as [`fetch`](https://developer.mozilla.or
9696
- The `credentials` option is `same-origin` by default, which is the default in the spec too, but not all browsers have caught up yet.
9797
- Adds some more options. See below.
9898

99-
Returns a [`Response` object](https://developer.mozilla.org/en-US/docs/Web/API/Response) with [`Body` methods](https://developer.mozilla.org/en-US/docs/Web/API/Body#Methods) added for convenience. So you can, for example, call `ky.get(input).json()` directly without having to await the `Response` first. When called like that, an appropriate `Accept` header will be set depending on the body method used. Unlike the `Body` methods of `window.Fetch`; these will throw an `HTTPError` if the response status is not in the range of `200...299`. Also, `.json()` will return an empty string if the response status is `204` instead of throwing a parse error due to an empty body.
99+
Returns a [`Response` object](https://developer.mozilla.org/en-US/docs/Web/API/Response) with [`Body` methods](https://developer.mozilla.org/en-US/docs/Web/API/Body#Methods) added for convenience. So you can, for example, call `ky.get(input).json()` directly without having to await the `Response` first. When called like that, an appropriate `Accept` header will be set depending on the body method used. Unlike the `Body` methods of `window.Fetch`; these will throw an `HTTPError` if the response status is not in the range of `200...299`. Also, `.json()` will return an empty string if body is empty or the response status is `204` instead of throwing a parse error due to an empty body.
100100

101101
### ky.get(input, options?)
102102
### ky.post(input, options?)

‎source/core/Ky.ts

+5
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ export class Ky {
9191
return '';
9292
}
9393

94+
const contentLength = response.headers.get('Content-Length');
95+
if (contentLength === null || contentLength === '0') {
96+
return '';
97+
}
98+
9499
if (options.parseJson) {
95100
return options.parseJson(await response.text());
96101
}

‎source/types/ResponsePromise.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
Returns a `Response` object with `Body` methods added for convenience. So you can, for example, call `ky.get(input).json()` directly without having to await the `Response` first. When called like that, an appropriate `Accept` header will be set depending on the body method used. Unlike the `Body` methods of `window.Fetch`; these will throw an `HTTPError` if the response status is not in the range of `200...299`. Also, `.json()` will return an empty string if the response status is `204` instead of throwing a parse error due to an empty body.
2+
Returns a `Response` object with `Body` methods added for convenience. So you can, for example, call `ky.get(input).json()` directly without having to await the `Response` first. When called like that, an appropriate `Accept` header will be set depending on the body method used. Unlike the `Body` methods of `window.Fetch`; these will throw an `HTTPError` if the response status is not in the range of `200...299`. Also, `.json()` will return an empty string if body is empty or the response status is `204` instead of throwing a parse error due to an empty body.
33
*/
44
import {KyResponse} from './response.js';
55

‎test/main.ts

+19-5
Original file line numberDiff line numberDiff line change
@@ -236,18 +236,32 @@ test('.json() with custom accept header', async t => {
236236
await server.close();
237237
});
238238

239-
test('.json() with 200 response and empty body', async t => {
239+
test('.json() with invalid JSON body', async t => {
240+
const server = await createHttpTestServer();
241+
server.get('/', async (request, response) => {
242+
t.is(request.headers.accept, 'application/json');
243+
response.end('not json');
244+
});
245+
246+
await t.throwsAsync(ky.get(server.url).json(), {
247+
message: /Unexpected token/,
248+
});
249+
250+
await server.close();
251+
});
252+
253+
test('.json() with empty body', async t => {
240254
t.plan(2);
241255

242256
const server = await createHttpTestServer();
243257
server.get('/', async (request, response) => {
244258
t.is(request.headers.accept, 'application/json');
245-
response.status(200).end();
259+
response.end();
246260
});
247261

248-
await t.throwsAsync(ky(server.url).json(), {
249-
message: /Unexpected end of JSON input/,
250-
});
262+
const responseJson = await ky.get(server.url).json();
263+
264+
t.is(responseJson, '');
251265

252266
await server.close();
253267
});

0 commit comments

Comments
 (0)
Please sign in to comment.