Skip to content

Commit 3c94692

Browse files
kevinoidbuunguyen
authored andcommitted
[PATCH v2] Include .response property on Errors as documented (#1039)
When fetch fails as the result of a server error, callers may want to examine the headers and/or content of the response to determine the nature of the error. Provide this information in the .response property on the rejection Error object as currently documented in the HTTP Client section of README.md. Changes in v2: - Apply responseInterceptor to error responses - Add tests to cover responseInterceptor behavior on error responses Signed-off-by: Kevin Locke <[email protected]>
1 parent ecb3417 commit 3c94692

File tree

2 files changed

+79
-6
lines changed

2 files changed

+79
-6
lines changed

src/http.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,29 @@ export default function http(url, request = {}) {
3737
}
3838

3939
return fetch(request.url, request).then((res) => {
40-
if (!res.ok) {
41-
const error = new Error(res.statusText)
42-
error.statusCode = error.status = res.status
43-
throw error
44-
}
45-
return self.serializeRes(res, url, request).then((_res) => {
40+
const serialized = self.serializeRes(res, url, request).then((_res) => {
4641
if (request.responseInterceptor) {
4742
_res = request.responseInterceptor(_res) || _res
4843
}
4944
return _res
5045
})
46+
47+
if (!res.ok) {
48+
const error = new Error(res.statusText)
49+
error.statusCode = error.status = res.status
50+
return serialized.then(
51+
(_res) => {
52+
error.response = _res
53+
throw error
54+
},
55+
(resError) => {
56+
error.responseError = resError
57+
throw error
58+
}
59+
)
60+
}
61+
62+
return serialized
5163
})
5264
}
5365

test/http.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,67 @@ describe('http', () => {
3838
})
3939
})
4040

41+
it('should include status code and response with HTTP Error', () => {
42+
xapp = xmock()
43+
xapp.get('http://swagger.io', (req, res) => res.status(400).send('hi'))
44+
45+
return http({
46+
url: 'http://swagger.io'
47+
})
48+
.then(
49+
(res) => {
50+
throw new Error('Expected rejection for HTTP status 400')
51+
},
52+
(err) => {
53+
expect(err.status).toEqual(400)
54+
expect(err.statusCode).toEqual(400)
55+
expect(err.response.text).toEqual('hi')
56+
}
57+
)
58+
})
59+
60+
it('should apply responseInterceptor to error responses', () => {
61+
xapp = xmock()
62+
xapp.get('http://swagger.io', (req, res) => res.status(400).send('hi'))
63+
64+
return http({
65+
url: 'http://swagger.io',
66+
responseInterceptor: (res) => {
67+
res.testValue = 5
68+
}
69+
})
70+
.then(
71+
(res) => {
72+
throw new Error('Expected rejection for HTTP status 400')
73+
},
74+
(err) => {
75+
expect(err.response.testValue).toEqual(5)
76+
}
77+
)
78+
})
79+
80+
it('should set responseError on responseInterceptor Error', () => {
81+
xapp = xmock()
82+
xapp.get('http://swagger.io', (req, res) => res.status(400).send('hi'))
83+
84+
const testError = new Error()
85+
return http({
86+
url: 'http://swagger.io',
87+
responseInterceptor: (res) => {
88+
throw testError
89+
}
90+
})
91+
.then(
92+
(res) => {
93+
throw new Error('Expected rejection for HTTP status 400')
94+
},
95+
(err) => {
96+
expect(err.response).toEqual(null)
97+
expect(err.responseError).toBe(testError)
98+
}
99+
)
100+
})
101+
41102
describe('serializeHeaders', function () {
42103
it('should handle FetchAPI Headers object, which is iterable', function () {
43104
// Given

0 commit comments

Comments
 (0)