Skip to content

Commit a60e79d

Browse files
Add 'content-type' ResponseHandler (#2363)
Co-authored-by: Lenz Weber <mail@lenzw.de>
1 parent 59dfcd2 commit a60e79d

File tree

2 files changed

+135
-18
lines changed

2 files changed

+135
-18
lines changed

packages/toolkit/src/query/fetchBaseQuery.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { BaseQueryApi, BaseQueryFn } from './baseQueryTypes'
44
import type { MaybePromise, Override } from './tsHelpers'
55

66
export type ResponseHandler =
7+
| 'content-type'
78
| 'json'
89
| 'text'
910
| ((response: Response) => Promise<any>)
@@ -41,24 +42,6 @@ const defaultValidateStatus = (response: Response) =>
4142
const defaultIsJsonContentType = (headers: Headers) =>
4243
/*applicat*/ /ion\/(vnd\.api\+)?json/.test(headers.get('content-type') || '')
4344

44-
const handleResponse = async (
45-
response: Response,
46-
responseHandler: ResponseHandler
47-
) => {
48-
if (typeof responseHandler === 'function') {
49-
return responseHandler(response)
50-
}
51-
52-
if (responseHandler === 'text') {
53-
return response.text()
54-
}
55-
56-
if (responseHandler === 'json') {
57-
const text = await response.text()
58-
return text.length ? JSON.parse(text) : null
59-
}
60-
}
61-
6245
export type FetchBaseQueryError =
6346
| {
6447
/**
@@ -342,4 +325,24 @@ export function fetchBaseQuery({
342325
meta,
343326
}
344327
}
328+
329+
async function handleResponse(
330+
response: Response,
331+
responseHandler: ResponseHandler
332+
) {
333+
if (typeof responseHandler === 'function') {
334+
return responseHandler(response)
335+
}
336+
337+
if (responseHandler === 'content-type') {
338+
responseHandler = isJsonContentType(response.headers) ? 'json' : 'text'
339+
}
340+
341+
if (responseHandler === 'json') {
342+
const text = await response.text()
343+
return text.length ? JSON.parse(text) : null
344+
}
345+
346+
return response.text()
347+
}
345348
}

packages/toolkit/src/query/tests/fetchBaseQuery.test.tsx

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,64 @@ describe('fetchBaseQuery', () => {
181181
})
182182
})
183183

184+
it('success: parse text without error ("content-type" responseHandler)', async () => {
185+
server.use(
186+
rest.get('https://example.com/success', (_, res, ctx) =>
187+
res.once(
188+
ctx.text(`this is not json!`)
189+
// NOTE: MSW sets content-type header as text automatically
190+
)
191+
)
192+
)
193+
194+
const req = baseQuery(
195+
{
196+
url: '/success',
197+
responseHandler: 'content-type',
198+
},
199+
commonBaseQueryApi,
200+
{}
201+
)
202+
expect(req).toBeInstanceOf(Promise)
203+
const res = await req
204+
expect(res).toBeInstanceOf(Object)
205+
expect(res.meta?.response?.headers.get('content-type')).toEqual(
206+
'text/plain'
207+
)
208+
expect(res.meta?.request).toBeInstanceOf(Request)
209+
expect(res.meta?.response).toBeInstanceOf(Object)
210+
expect(res.data).toEqual(`this is not json!`)
211+
})
212+
213+
it('success: parse json without error ("content-type" responseHandler)', async () => {
214+
server.use(
215+
rest.get('https://example.com/success', (_, res, ctx) =>
216+
res.once(
217+
ctx.json(`this will become json!`)
218+
// NOTE: MSW sets content-type header as json automatically
219+
)
220+
)
221+
)
222+
223+
const req = baseQuery(
224+
{
225+
url: '/success',
226+
responseHandler: 'content-type',
227+
},
228+
commonBaseQueryApi,
229+
{}
230+
)
231+
expect(req).toBeInstanceOf(Promise)
232+
const res = await req
233+
expect(res).toBeInstanceOf(Object)
234+
expect(res.meta?.response?.headers.get('content-type')).toEqual(
235+
'application/json'
236+
)
237+
expect(res.meta?.request).toBeInstanceOf(Request)
238+
expect(res.meta?.response).toBeInstanceOf(Object)
239+
expect(res.data).toEqual(`this will become json!`)
240+
})
241+
184242
it('server error: should fail normally with a 500 status ("text" responseHandler)', async () => {
185243
server.use(
186244
rest.get('https://example.com/error', (_, res, ctx) =>
@@ -204,6 +262,62 @@ describe('fetchBaseQuery', () => {
204262
})
205263
})
206264

265+
it('server error: should fail normally with a 500 status as text ("content-type" responseHandler)', async () => {
266+
const serverResponse = 'Internal Server Error'
267+
server.use(
268+
rest.get('https://example.com/error', (_, res, ctx) =>
269+
res(ctx.status(500), ctx.text(serverResponse))
270+
)
271+
)
272+
273+
const req = baseQuery(
274+
{ url: '/error', responseHandler: 'content-type' },
275+
commonBaseQueryApi,
276+
{}
277+
)
278+
expect(req).toBeInstanceOf(Promise)
279+
const res = await req
280+
expect(res).toBeInstanceOf(Object)
281+
expect(res.meta?.request).toBeInstanceOf(Request)
282+
expect(res.meta?.response).toBeInstanceOf(Object)
283+
expect(res.meta?.response?.headers.get('content-type')).toEqual(
284+
'text/plain'
285+
)
286+
expect(res.error).toEqual({
287+
status: 500,
288+
data: serverResponse,
289+
})
290+
})
291+
292+
it('server error: should fail normally with a 500 status as json ("content-type" responseHandler)', async () => {
293+
const serverResponse = {
294+
errors: { field1: "Password cannot be 'password'" },
295+
}
296+
server.use(
297+
rest.get('https://example.com/error', (_, res, ctx) =>
298+
res(ctx.status(500), ctx.json(serverResponse))
299+
)
300+
)
301+
302+
const req = baseQuery(
303+
{ url: '/error', responseHandler: 'content-type' },
304+
commonBaseQueryApi,
305+
{}
306+
)
307+
expect(req).toBeInstanceOf(Promise)
308+
const res = await req
309+
expect(res).toBeInstanceOf(Object)
310+
expect(res.meta?.request).toBeInstanceOf(Request)
311+
expect(res.meta?.response).toBeInstanceOf(Object)
312+
expect(res.meta?.response?.headers.get('content-type')).toEqual(
313+
'application/json'
314+
)
315+
expect(res.error).toEqual({
316+
status: 500,
317+
data: serverResponse,
318+
})
319+
})
320+
207321
it('server error: should fail gracefully (default="json" responseHandler)', async () => {
208322
server.use(
209323
rest.get('https://example.com/error', (_, res, ctx) =>

0 commit comments

Comments
 (0)