Improve client XHR error handling

The default XHR error handler produce an alert which is appropriate to the response status code. It can be overridden per-request (by specifying the `errorHandler` option) so that the alert can be suppressed or displayed in a different position (e.g. inside a modal).

ref #118
This commit is contained in:
Toby Zerner
2015-10-20 12:48:26 +10:30
parent 7490709af8
commit 26a821e3e2
26 changed files with 192 additions and 175 deletions

View File

@ -206,10 +206,14 @@ export default class App {
try {
return JSON.parse(responseText);
} catch (e) {
throw new RequestError(e.message, responseText);
throw new RequestError(500, responseText);
}
});
options.errorHandler = options.errorHandler || (error => {
throw error;
});
// When extracting the data from the response, we can check the server
// response code and show an error message to the user if something's gone
// awry.
@ -225,26 +229,56 @@ export default class App {
const status = xhr.status;
if (status >= 500 && status <= 599) {
throw new RequestError('Internal Server Error', responseText);
if (status < 200 || status > 299) {
throw new RequestError(status, responseText, xhr);
}
return responseText;
};
this.alerts.dismiss(this.requestErrorAlert);
if (this.requestError) this.requestError.hideAlert();
// Now make the request. If it's a failure, inspect the error that was
// returned and show an alert containing its contents.
return m.request(options).then(null, error => {
if (error instanceof RequestError) {
this.alerts.show(this.requestErrorAlert = new Alert({
type: 'error',
children: 'Oops! Something went wrong. Please reload the page and try again.',
controls: app.forum.attribute('debug') ? [
<Button className="Button Button--link" onclick={this.showDebug.bind(this, error)}>Debug</Button>
] : undefined
}));
this.requestError = error;
let children;
switch (error.status) {
case 422:
children = error.response.errors
.map(error => [error.detail, <br/>])
.reduce((a, b) => a.concat(b), [])
.slice(0, -1);
break;
case 401:
case 403:
children = 'You do not have permission to do that.';
break;
case 404:
case 410:
children = 'The requested resource was not found.';
break;
default:
children = 'Oops! Something went wrong. Please reload the page and try again.';
}
error.alert = new Alert({
type: 'error',
children,
controls: app.forum.attribute('debug') ? [
<Button className="Button Button--link" onclick={this.showDebug.bind(this, error)}>Debug</Button>
] : undefined
});
try {
options.errorHandler(error);
} catch (error) {
this.alerts.show(error.alert);
}
throw error;
@ -264,16 +298,18 @@ export default class App {
/**
* Show alert error messages for each error returned in an API response.
*
* @param {Array} errors
* @param {Object} response
* @public
*/
alertErrors(errors) {
errors.forEach(error => {
this.alerts.show(new Alert({
type: 'error',
children: error.detail
}));
});
alertErrors(response) {
if (response.errors) {
response.errors.forEach(error => {
this.alerts.show(new Alert({
type: 'error',
children: error.detail
}));
});
}
}
/**