Skip to content

Instantly share code, notes, and snippets.

@laser
Last active May 16, 2016 20:54
Show Gist options
  • Save laser/66339aa7117b228d108d2cd72cf55bc1 to your computer and use it in GitHub Desktop.
Save laser/66339aa7117b228d108d2cd72cf55bc1 to your computer and use it in GitHub Desktop.
Error Proposal

Proposal: JSON Error Response-Bodies

This document serves as a proposal for how CJ might represent web service errors as JSON response-bodies.

Contents

Example Swagger 2.0 User Schema

A Swagger 2.0 schema for a User looks like this:

"User": {
  "type": "object",
  "required": [
    "fullName",
    "emailAddress",
    "tags"
  ],
  "properties": {
    "fullName": {
      "type": "string",
      "minLength": 4
    },
    "emailAddress": {
      "type": "string",
      "description": "must be unique across all users"
    },
    "birthday": {
      "type": "number",
      "description": "YYYYMMDD-formatted date, e.g. 20150601"
    },
    "tags": {
      "type": "array",
      "items": {
          "type": "string",
          "enum": [
            "friendly",
            "hostile",
            "happy",
            "sad"
          ]
       }
    }
  }
}

Example Swagger 2.0 Error Schema

A Swagger 2.0 schema for a Error looks like this:

"Error": {
  "type": "object",
  "required": [
    "code"
  ],
  "properties": {
    "field": {
      "type": "string",
      "description": "A simple JMESPath query, pointing to a single offending field"
    },
    "code": {
      "type": "string",
      "description": "An code explaining the nature of the error"
    },
    "description": {
      "type": "string",
      "description": "must be unique across all users"
    }
  }
}

Examples of HTTP 400 Bad Request (top level object)

Imagine a URL http://example.com/users that, when POST'ed to, creates a single User. To successfully create a User, the inbound JSON must conform to the User schema shown above. In addition to this, each Users in our system must have a unique email address.

Missing Field

Example Request
HTTP 1.1 POST /users

{
  "fullName": "Sally Smith",
  "birthDate": 19820601,
  "tags": []
}
Example Response
HTTP 400 Bad Request

{
  "errors": [
    {
      "field": "emailAddress",
      "code": "general.missing",
      "description": "Email address was not provided"
    }
  ]
}

Invalid Field Data (single field - too short)

An HTTP client attempts to create a User but provides a full name that is too short:

Example Request
HTTP 1.1 POST /users

{
  "fullName": "Sa",
  "emailAddress": "[email protected]",
  "tags": []
}
Example Response
HTTP 400 Bad Request

{
  "errors": [
    {
      "field": "fullName",
      "code": "general.tooShort",
      "description": "Full name must be longer than 3 characters"
    }
  ]
}

Invalid Field Data (two fields in bad state)

An HTTP client attempts to create a User but provides a malformed email address and a full name that is too short:

Example Request
HTTP 1.1 POST /users

{
  "fullName": "Sa",
  "emailAddress": "asdasdasd",
  "tags": []
}
Example Response
HTTP 400 Bad Request

{
  "errors": [
    {
      "field": "fullName",
      "code": "general.tooShort",
      "description": "Full name must be longer than 3 characters"
    },
    {
      "field": "emailAddress",
      "code": "general.notValid",
      "description": "Email address not valid"
    }
  ]
}

Invalid Field Data (business rule-violation)

An API client attempts to create a User with an email address that would violate a uniqueness constraint:

Example Request
HTTP 1.1 POST /users

{
  "fullName": "Sally Smith",
  "emailAddress": "[email protected]",
  "tags": []
}
Example Response
HTTP 400 Bad Request

{
  "errors": [
    {
      "field": "emailAddress",
      "code": "general.alreadyExists",
      "description": "A user with that email address already exists"
    }
  ]
}

Invalid Field Data (array property)

An API client attempts to create a User, providing an unsupported tag.

Example Request
HTTP 1.1 POST /users

{
  "fullName": "Sally Smith",
  "emailAddress": "[email protected]",
  "tags": ["happy", "morose"]
}
Example Response
HTTP 400 Bad Request

{
  "errors": [
    {
      "field": "tags[1]",
      "code": "general.notInEnum",
      "description": "\"morose\" is not a valid tag"
    }
  ]
}

Examples of Bad Request (top level array; batch endpoint)

Imagine a URL http://example.com/users/batch that, when POST'ed to, creates multiple Users. To successfully create Users, the inbound JSON must be a JSON array whose elements are JSON objects that conform to the User schema shown above. In addition to this, each Users in our system must have a unique email address.

Invalid Field Data (bad fields in each object)

Example Request
HTTP 1.1 POST /users/batch

[
  {
    "fullName": "Sa",
    "emailAddress": "[email protected]",
    "tags": []
  },
  {
    "fullName": "Jimmy John",
    "emailAddress" "delicious.sandw",
    "tags": ["morose"]
  }
]
Example Response
HTTP 400 Bad Request

{
  "errors": [
    {
      "field": "[0].fullName",
      "code": "general.tooShort",
      "description": "Full name must be longer than 3 characters"
    },
    {
      "field": "[1].emailAddress",
      "code": "general.invalid",
      "description": "Email address not valid"
    },
    {
      "field": "[1].tags[0]",
      "code": "general.notInEnum",
      "description": "\"morose\" is not a valid tag"
    }
  ]
}

HTTP 400 (no single field was bad)

Some requests failed to be processed not because of a particular field - but due to something higher level (e.g. maximum number of application key already provisioned for given developer). In that case, the field property is omitted from the response.

Too Many Users Already Created

Example Request
HTTP 1.1 POST /users

{
  "fullName": "Sally Smith",
  "birthDate": 19820601,
  "emailAddress": "[email protected]",
  "tags": []
}
Example Response
HTTP 400 Bad Request

{
  "errors": [
    {
      "message": "Too many users created",
      "code": "ad.users.tooMany"
    }
  ]
}

i18n

CJ occasionally provides multi-language support in its APIs. As an alternative to providing a description property whose value is an EN_US string, a code property may be provided whose value corresponds to a key in our internationalization system (e.g. ad.advertiser.archiveUrl.length.exceeded in cjo/content/src/main/resources/cj/cjo/language/content.properties).

Missing Field

An HTTP client attempts to create a User but does not send an email address:

Example Request
HTTP 1.1 POST /users

{
  "fullName": "Sally Smith",
  "birthDate": 19820601,
  "tags": []
}
Example Response
HTTP 400 Bad Request

{
  "errors": [
    {
      "field": "emailAddress",
      "message": "Email address is missing",
      "code": "ad.users.emailAddress.missing"
    }
  ]
}
@aztecrex
Copy link

This is great. I suggest that we return an error code instead of a message--like your final example on localization. I suggest that the message displayed to a user is part of the app, not the resource. OTOH, it is nice to have something readable for troubleshooting so maybe we include an informational message, not considered part of the contract, along with the code.

The path language you show for identifying the problem data element is similar to JMESPath. We might want to standardize on that so we leverage both code and documentation resources already available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment