Bring some sanity to request parsing and error handling (#2335)

* Update docs/request.md
* Bring some sanity to request parsing and error handling

- The browser can do JSON parsing itself. Let's defer to that where
  possible. (A few IE hacks are required here, though.)
- Don't propagate any error that occurs before `deserialize`/`extract`.
- Allow sending raw array buffers/blobs/etc. to `deserialize`.
- Align behavior more closely with the XHR spec.
- Send the more useful parsed response to `deserialize`, not the less
  useful string response.
This commit is contained in:
Isiah Meadows 2019-05-29 09:41:22 -04:00 committed by GitHub
parent 09afc54c7b
commit 794e8e963f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 55 additions and 18 deletions

View file

@ -79,7 +79,7 @@ module.exports = function($window, Promise) {
}
if (args.withCredentials) xhr.withCredentials = args.withCredentials
if (args.timeout) xhr.timeout = args.timeout
if (args.responseType) xhr.responseType = args.responseType
xhr.responseType = args.responseType || (typeof args.extract === "function" ? "" : "json")
for (var key in args.headers) {
if ({}.hasOwnProperty.call(args.headers, key)) {
@ -96,19 +96,38 @@ module.exports = function($window, Promise) {
if (xhr.readyState === 4) {
try {
var success = (xhr.status >= 200 && xhr.status < 300) || xhr.status === 304 || (/^file:\/\//i).test(url)
var response = xhr.responseText
// When the response type isn't "" or "text",
// `xhr.responseText` is the wrong thing to use.
// Browsers do the right thing and throw here, and we
// should honor that and do the right thing by
// preferring `xhr.response` where possible/practical.
var response = xhr.response, message
if (response == null) {
try {
response = xhr.responseText
// Note: this snippet is intentionally *after*
// `xhr.responseText` is accessed, since the
// above will throw in modern browsers (thus
// skipping the rest of this section). It's an
// IE hack to detect and work around the lack of
// native `responseType: "json"` support there.
if (typeof args.extract !== "function" && xhr.responseType === "json") response = JSON.parse(response)
}
catch (e) { response = null }
}
if (typeof args.extract === "function") {
response = args.extract(xhr, args)
success = true
} else if (typeof args.deserialize === "function") {
response = args.deserialize(response)
} else {
try {response = response ? JSON.parse(response) : null}
catch (e) {throw new Error("Invalid JSON: " + response)}
}
if (success) resolve(response)
else {
var error = new Error(xhr.responseText)
try { message = xhr.responseText }
catch (e) { message = response }
var error = new Error(message)
error.code = xhr.status
error.response = response
reject(error)

View file

@ -290,7 +290,7 @@ o.spec("xhr", function() {
}
})
xhr({method: "GET", url: "/item", deserialize: deserialize}).then(function(data) {
o(data).equals("{\"test\":123}")
o(data).deepEquals({test: 123})
}).then(done)
})
o("deserialize parameter works in POST", function(done) {
@ -304,12 +304,12 @@ o.spec("xhr", function() {
}
})
xhr({method: "POST", url: "/item", deserialize: deserialize}).then(function(data) {
o(data).equals("{\"test\":123}")
o(data).deepEquals({test: 123})
}).then(done)
})
o("extract parameter works in GET", function(done) {
var extract = function() {
return JSON.stringify({test: 123})
return {test: 123}
}
mock.$defineRoutes({
@ -318,12 +318,12 @@ o.spec("xhr", function() {
}
})
xhr({method: "GET", url: "/item", extract: extract}).then(function(data) {
o(data).equals("{\"test\":123}")
o(data).deepEquals({test: 123})
}).then(done)
})
o("extract parameter works in POST", function(done) {
var extract = function() {
return JSON.stringify({test: 123})
return {test: 123}
}
mock.$defineRoutes({
@ -332,7 +332,7 @@ o.spec("xhr", function() {
}
})
xhr({method: "POST", url: "/item", extract: extract}).then(function(data) {
o(data).equals("{\"test\":123}")
o(data).deepEquals({test: 123})
}).then(done)
})
o("ignores deserialize if extract is defined", function(done) {
@ -545,7 +545,8 @@ o.spec("xhr", function() {
})
xhr({method: "GET", url: "/item"}).catch(function(e) {
o(e instanceof Error).equals(true)
o(e.message).equals(JSON.stringify({error: "error"}))
o(e.message).equals("[object Object]")
o(e.response).deepEquals({error: "error"})
o(e.code).equals(500)
}).then(done)
})
@ -568,7 +569,8 @@ o.spec("xhr", function() {
}
})
xhr({method: "GET", url: "/item"}).catch(function(e) {
o(e.message).equals("Invalid JSON: error")
o(e.message).equals("null")
o(e.response).equals(null)
}).then(done)
})
o("triggers all branched catches upon rejection", function(done) {