Skip to content

Commit 169d5e0

Browse files
pablobmbmac
authored andcommitted
[DOC] Detail handling of server validation errors
(cherry picked from commit 648c7b8)
1 parent 3473a10 commit 169d5e0

File tree

3 files changed

+105
-55
lines changed

3 files changed

+105
-55
lines changed

addon/-private/system/model/errors.js

Lines changed: 10 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ var MapWithDefault = Ember.MapWithDefault;
1313
*/
1414

1515
/**
16-
Holds validation errors for a given record organized by attribute names.
16+
Holds validation errors for a given record, organized by attribute names.
1717
18-
Every DS.Model has an `errors` property that is an instance of
18+
Every `DS.Model` has an `errors` property that is an instance of
1919
`DS.Errors`. This can be used to display validation error
2020
messages returned from the server when a `record.save()` rejects.
2121
22-
For Example, if you had an `User` model that looked like this:
22+
For Example, if you had a `User` model that looked like this:
2323
2424
```app/models/user.js
2525
import DS from 'ember-data';
@@ -29,7 +29,7 @@ var MapWithDefault = Ember.MapWithDefault;
2929
email: attr('string')
3030
});
3131
```
32-
And you attempted to save a record that did not validate on the backend.
32+
And you attempted to save a record that did not validate on the backend:
3333
3434
```javascript
3535
var user = store.createRecord('user', {
@@ -39,28 +39,13 @@ var MapWithDefault = Ember.MapWithDefault;
3939
user.save();
4040
```
4141
42-
Your backend data store might return a response with status code 422 (Unprocessable Entity)
43-
and that looks like this. This response will be used to populate the error object.
42+
Your backend would be expected to return an error response that described
43+
the problem, so that error messages can be generated on the app.
4444
45-
```javascript
46-
{
47-
"errors": [
48-
{
49-
"detail": "This username is already taken!",
50-
"source": {
51-
"pointer": "data/attributes/username"
52-
}
53-
}, {
54-
"detail": "Doesn't look like a valid email.",
55-
"source": {
56-
"pointer": "data/attributes/email"
57-
}
58-
}
59-
]
60-
}
61-
```
62-
63-
For additional information on the error object, see the [JSON API spec](http://jsonapi.org/format/#error-objects).
45+
API responses will be translated into instances of `DS.Errors` differently,
46+
depending on the specific combination of adapter and serializer used. You
47+
may want to check the documentation or the source code of the libraries
48+
that you are using, to know how they expect errors to be communicated.
6449
6550
Errors can be displayed to the user by accessing their property name
6651
to get an array of all the error objects for that property. Each
@@ -96,33 +81,6 @@ var MapWithDefault = Ember.MapWithDefault;
9681
{{/each}}
9782
```
9883
99-
The JSON API spec also allows for object level errors to be placed
100-
in an object with pointer `data`.
101-
102-
```javascript
103-
{
104-
"errors": [
105-
{
106-
"detail": "Some generic non property error message",
107-
"source": {
108-
"pointer": "data"
109-
}
110-
}
111-
]
112-
}
113-
```
114-
115-
You can access these errors by using the `base` property on the errors
116-
object.
117-
118-
```handlebars
119-
{{#each model.errors.base as |error|}}
120-
<div class="error">
121-
{{error.message}}
122-
</div>
123-
{{/each}}
124-
```
125-
12684
@class Errors
12785
@namespace DS
12886
@extends Ember.Object

addon/adapters/rest.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,24 @@ const {
2727
This adapter is designed around the idea that the JSON exchanged with
2828
the server should be conventional.
2929
30+
## Success and failure
31+
32+
The REST adapter will consider a success any response with a status code
33+
of the 2xx family ("Success"), as well as 304 ("Not Modified"). Any other
34+
status code will be considered a failure.
35+
36+
On success, the request promise will be resolved with the full response
37+
payload.
38+
39+
Failed responses with status code 422 ("Unprocessable Entity") will be
40+
considered "invalid". The response will be discarded, except for the
41+
`errors` key. The request promise will be rejected with a `DS.InvalidError`.
42+
This error object will encapsulate the saved `errors` value.
43+
44+
Any other status codes will be treated as an "adapter error". The request
45+
promise will be rejected, similarly to the "invalid" case, but with
46+
an instance of `DS.AdapterError` instead.
47+
3048
## JSON Structure
3149
3250
The REST adapter expects the JSON returned from your server to follow
@@ -104,6 +122,24 @@ const {
104122
}
105123
```
106124
125+
### Errors
126+
127+
If a response is considered a failure, the JSON payload is expected to include
128+
a top-level key `errors`, detailing any specific issues. For example:
129+
130+
```js
131+
{
132+
"errors": {
133+
"msg": "Something went wrong"
134+
}
135+
}
136+
```
137+
138+
This adapter does not make any assumptions as to the format of the `errors`
139+
object. It will simply be passed along as is, wrapped in an instance
140+
of `DS.InvalidError` or `DS.AdapterError`. The serializer can interpret it
141+
afterwards.
142+
107143
## Customization
108144
109145
### Endpoint path customization

addon/serializers/json.js

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1270,12 +1270,68 @@ export default Serializer.extend({
12701270
},
12711271

12721272
/**
1273-
`extractErrors` is used to extract model errors when a call is made
1274-
to `DS.Model#save` which fails with an `InvalidError`. By default
1273+
`extractErrors` is used to extract model errors when a call
1274+
to `DS.Model#save` fails with an `InvalidError`. By default
12751275
Ember Data expects error information to be located on the `errors`
12761276
property of the payload object.
12771277
1278-
Example
1278+
This serializer expects this `errors` object to be an Array similar
1279+
to the following, compliant with the JSON-API specification:
1280+
1281+
```js
1282+
{
1283+
"errors": [
1284+
{
1285+
"detail": "This username is already taken!",
1286+
"source": {
1287+
"pointer": "data/attributes/username"
1288+
}
1289+
}, {
1290+
"detail": "Doesn't look like a valid email.",
1291+
"source": {
1292+
"pointer": "data/attributes/email"
1293+
}
1294+
}
1295+
]
1296+
}
1297+
```
1298+
1299+
The key `detail` provides a textual description of the problem.
1300+
Alternatively, the key `title` can be used for the same purpose.
1301+
1302+
The nested keys `source.pointer` detail which specific element
1303+
of the request data was invalid.
1304+
1305+
Note that JSON-API also allows for object-level errors to be placed
1306+
in an object with pointer `data`, signifying that the problem
1307+
cannot be traced to a specific attribute:
1308+
1309+
```javascript
1310+
{
1311+
"errors": [
1312+
{
1313+
"detail": "Some generic non property error message",
1314+
"source": {
1315+
"pointer": "data"
1316+
}
1317+
}
1318+
]
1319+
}
1320+
```
1321+
1322+
When turn into a `DS.Errors` object, you can read these errors
1323+
through the property `base`:
1324+
1325+
```handlebars
1326+
{{#each model.errors.base as |error|}}
1327+
<div class="error">
1328+
{{error.message}}
1329+
</div>
1330+
{{/each}}
1331+
```
1332+
1333+
Example of alternative implementation, overriding the default
1334+
behavior to deal with a different format of errors:
12791335
12801336
```app/serializers/post.js
12811337
import DS from 'ember-data';

0 commit comments

Comments
 (0)