diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..5f39ef9
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,9 @@
+{
+    "name": "drupal/jsonapi",
+    "description": "Provides a JSON API format for the REST resources.",
+    "type": "drupal-module",
+    "license": "GPL-2.0+",
+    "require-dev": {
+        "justinrainbow/json-schema": "^4.1"
+    }
+}
diff --git a/schema.json b/schema.json
new file mode 100644
index 0000000..dfbe0af
--- /dev/null
+++ b/schema.json
@@ -0,0 +1,375 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "JSON API Schema",
+  "description": "This is a schema for responses in the JSON API format. For more, see http://jsonapi.org",
+  "oneOf": [
+    {
+      "$ref": "#/definitions/success"
+    },
+    {
+      "$ref": "#/definitions/failure"
+    },
+    {
+      "$ref": "#/definitions/info"
+    }
+  ],
+  
+  "definitions": {
+    "success": {
+      "type": "object",
+      "required": [
+        "data"
+      ],
+      "properties": {
+        "data": {
+          "$ref": "#/definitions/data"
+        },
+        "included": {
+          "description": "To reduce the number of HTTP requests, servers **MAY** allow responses that include related resources along with the requested primary resources. Such responses are called \"compound documents\".",
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/resource"
+          },
+          "uniqueItems": true
+        },
+        "meta": {
+          "$ref": "#/definitions/meta"
+        },
+        "links": {
+          "description": "Link members related to the primary data.",
+          "allOf": [
+            {
+              "$ref": "#/definitions/links"
+            },
+            {
+              "$ref": "#/definitions/pagination"
+            }
+          ]
+        },
+        "jsonapi": {
+          "$ref": "#/definitions/jsonapi"
+        }
+      },
+      "additionalProperties": false
+    },
+    "failure": {
+      "type": "object",
+      "required": [
+        "errors"
+      ],
+      "properties": {
+        "errors": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/error"
+          },
+          "uniqueItems": true
+        },
+        "meta": {
+          "$ref": "#/definitions/meta"
+        },
+        "jsonapi": {
+          "$ref": "#/definitions/jsonapi"
+        }
+      },
+      "additionalProperties": false
+    },
+    "info": {
+      "type": "object",
+      "required": [
+        "meta"
+      ],
+      "properties": {
+        "meta": {
+          "$ref": "#/definitions/meta"
+        },
+        "links": {
+          "$ref": "#/definitions/links"
+        },
+        "jsonapi": {
+          "$ref": "#/definitions/jsonapi"
+        }
+      },
+      "additionalProperties": false
+    },
+    
+    "meta": {
+      "description": "Non-standard meta-information that can not be represented as an attribute or relationship.",
+      "type": "object",
+      "additionalProperties": true
+    },
+    "data": {
+      "description": "The document's \"primary data\" is a representation of the resource or collection of resources targeted by a request.",
+      "oneOf": [
+        {
+          "$ref": "#/definitions/resource"
+        },
+        {
+          "description": "An array of resource objects, an array of resource identifier objects, or an empty array ([]), for requests that target resource collections.",
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/resource"
+          },
+          "uniqueItems": true
+        },
+        {
+          "description": "null if the request is one that might correspond to a single resource, but doesn't currently.",
+          "type": "null"
+        }
+      ]
+    },
+    "resource": {
+      "description": "\"Resource objects\" appear in a JSON API document to represent resources.",
+      "type": "object",
+      "required": [
+        "type",
+        "id"
+      ],
+      "properties": {
+        "type": {
+          "type": "string"
+        },
+        "id": {
+          "type": "string"
+        },
+        "attributes": {
+          "$ref": "#/definitions/attributes"
+        },
+        "relationships": {
+          "$ref": "#/definitions/relationships"
+        },
+        "links": {
+          "$ref": "#/definitions/links"
+        },
+        "meta": {
+          "$ref": "#/definitions/meta"
+        }
+      },
+      "additionalProperties": false
+    },
+    
+    "links": {
+      "description": "A resource object **MAY** contain references to other resource objects (\"relationships\"). Relationships may be to-one or to-many. Relationships can be specified by including a member in a resource's links object.",
+      "type": "object",
+      "properties": {
+        "self": {
+          "description": "A `self` member, whose value is a URL for the relationship itself (a \"relationship URL\"). This URL allows the client to directly manipulate the relationship. For example, it would allow a client to remove an `author` from an `article` without deleting the people resource itself.",
+          "type": "string",
+          "format": "uri"
+        },
+        "related": {
+          "$ref": "#/definitions/link"
+        }
+      },
+      "additionalProperties": true
+    },
+    "link": {
+      "description": "A link **MUST** be represented as either: a string containing the link's URL or a link object.",
+      "oneOf": [
+        {
+          "description": "A string containing the link's URL.",
+          "type": "string",
+          "format": "uri"
+        },
+        {
+          "type": "object",
+          "required": [
+            "href"
+          ],
+          "properties": {
+            "href": {
+              "description": "A string containing the link's URL.",
+              "type": "string",
+              "format": "uri"
+            },
+            "meta": {
+              "$ref": "#/definitions/meta"
+            }
+          }
+        }
+      ]
+    },
+
+    "attributes": {
+      "description": "Members of the attributes object (\"attributes\") represent information about the resource object in which it's defined.",
+      "type": "object",
+      "patternProperties": {
+        "^(?!relationships$|links$)\\w[-\\w_]*$": {
+          "description": "Attributes may contain any valid JSON value."
+        }
+      },
+      "additionalProperties": false
+    },
+
+    "relationships": {
+      "description": "Members of the relationships object (\"relationships\") represent references from the resource object in which it's defined to other resource objects.",
+      "type": "object",
+      "patternProperties": {
+        "^\\w[-\\w_]*$": {
+          "properties": {
+            "links": {
+              "$ref": "#/definitions/links"
+            },
+            "data": {
+              "description": "Member, whose value represents \"resource linkage\".",
+              "oneOf": [
+                {
+                  "$ref": "#/definitions/relationshipToOne"
+                },
+                {
+                  "$ref": "#/definitions/relationshipToMany"
+                }
+              ]
+            },
+            "meta": {
+              "$ref": "#/definitions/meta"
+            }
+          },
+          "anyOf": [
+            {"required": ["data"]},
+            {"required": ["meta"]},
+            {"required": ["links"]}
+          ],
+          "additionalProperties": false
+        }
+      },
+      "additionalProperties": false
+    },
+    "relationshipToOne": {
+      "description": "References to other resource objects in a to-one (\"relationship\"). Relationships can be specified by including a member in a resource's links object.",
+      "anyOf": [
+        {
+          "$ref": "#/definitions/empty"
+        },
+        {
+          "$ref": "#/definitions/linkage"
+        }
+      ]
+    },
+    "relationshipToMany": {
+      "description": "An array of objects each containing \"type\" and \"id\" members for to-many relationships.",
+      "type": "array",
+      "items": {
+        "$ref": "#/definitions/linkage"
+      },
+      "uniqueItems": true
+    },
+    "empty": {
+      "description": "Describes an empty to-one relationship.",
+      "type": "null"
+    },
+    "linkage": {
+      "description": "The \"type\" and \"id\" to non-empty members.",
+      "type": "object",
+      "required": [
+        "type",
+        "id"
+      ],
+      "properties": {
+        "type": {
+          "type": "string"
+        },
+        "id": {
+          "type": "string"
+        },
+        "meta": {
+          "$ref": "#/definitions/meta"
+        }
+      },
+      "additionalProperties": false
+    },
+    "pagination": {
+      "type": "object",
+      "properties": {
+        "first": {
+          "description": "The first page of data",
+          "oneOf": [
+            { "type": "string", "format": "uri" },
+            { "type": "null" }
+          ]
+        },
+        "last": {
+          "description": "The last page of data",
+          "oneOf": [
+            { "type": "string", "format": "uri" },
+            { "type": "null" }
+          ]
+        },
+        "prev": {
+          "description": "The previous page of data",
+          "oneOf": [
+            { "type": "string", "format": "uri" },
+            { "type": "null" }
+          ]
+        },
+        "next": {
+          "description": "The next page of data",
+          "oneOf": [
+            { "type": "string", "format": "uri" },
+            { "type": "null" }
+          ]
+        }
+      }
+    },
+    
+    "jsonapi": {
+      "description": "An object describing the server's implementation",
+      "type": "object",
+      "properties": {
+        "version": {
+          "type": "string"
+        },
+        "meta": {
+          "$ref": "#/definitions/meta"
+        }
+      },
+      "additionalProperties": false
+    },
+    
+    "error": {
+      "type": "object",
+      "properties": {
+        "id": {
+          "description": "A unique identifier for this particular occurrence of the problem.",
+          "type": "string"
+        },
+        "links": {
+          "$ref": "#/definitions/links"
+        },
+        "status": {
+          "description": "The HTTP status code applicable to this problem, expressed as a string value.",
+          "type": "string"
+        },
+        "code": {
+          "description": "An application-specific error code, expressed as a string value.",
+          "type": "string"
+        },
+        "title": {
+          "description": "A short, human-readable summary of the problem. It **SHOULD NOT** change from occurrence to occurrence of the problem, except for purposes of localization.",
+          "type": "string"
+        },
+        "detail": {
+          "description": "A human-readable explanation specific to this occurrence of the problem.",
+          "type": "string"
+        },
+        "source": {
+          "type": "object",
+          "properties": {
+            "pointer": {
+              "description": "A JSON Pointer [RFC6901] to the associated entity in the request document [e.g. \"/data\" for a primary data object, or \"/data/attributes/title\" for a specific attribute].",
+              "type": "string"
+            },
+            "parameter": {
+              "description": "A string indicating which query parameter caused the error.",
+              "type": "string"
+            }
+          }
+        },
+        "meta": {
+          "$ref": "#/definitions/meta"
+        }
+      },
+      "additionalProperties": false
+    }
+  }
+}
diff --git a/src/Access/CustomParameterNames.php b/src/Access/CustomParameterNames.php
index 7a89c56..cafc066 100644
--- a/src/Access/CustomParameterNames.php
+++ b/src/Access/CustomParameterNames.php
@@ -33,6 +33,8 @@ class CustomParameterNames implements AccessInterface {
   /**
    * Validates the JSONAPI parameters.
    *
+   * @see http://jsonapi.org/format/#document-member-names
+   *
    * @param string[] $json_api_params
    *   The JSONAPI parameters.
    *
diff --git a/src/Context/CurrentContext.php b/src/Context/CurrentContext.php
index bc3370d..bcd42a1 100644
--- a/src/Context/CurrentContext.php
+++ b/src/Context/CurrentContext.php
@@ -97,12 +97,11 @@ class CurrentContext {
    *   The JSON API provided parameter.
    */
   public function getJsonApiParameter($parameter_key) {
-    $params = $this
+    return $this
       ->requestStack
       ->getCurrentRequest()
       ->attributes
-      ->get('_json_api_params');
-    return (isset($params[$parameter_key])) ? $params[$parameter_key] : NULL;
+      ->get("_json_api_params[$parameter_key]", NULL, TRUE);
   }
 
   /**
diff --git a/src/Controller/EntityResource.php b/src/Controller/EntityResource.php
index a553acd..03eb30c 100644
--- a/src/Controller/EntityResource.php
+++ b/src/Controller/EntityResource.php
@@ -248,8 +248,8 @@ class EntityResource {
     // Instantiate the query for the filtering.
     $entity_type_id = $this->resourceType->getEntityTypeId();
 
-    $params = $request->attributes->get('_route_params');
-    $query = $this->getCollectionQuery($entity_type_id, $params['_json_api_params']);
+    $params = $request->attributes->get('_route_params[_json_api_params]', NULL, TRUE);
+    $query = $this->getCollectionQuery($entity_type_id, $params);
 
     $results = $query->execute();
 
@@ -644,8 +644,11 @@ class EntityResource {
     // The update is different for configuration entities and content entities.
     if ($origin instanceof ContentEntityInterface && $destination instanceof ContentEntityInterface) {
       // First scenario: both are content entities.
-      if (!$destination_field_list = $destination->get($field_name)) {
-        throw new SerializableHttpException(400, sprintf('The provided field (%s) does not exist in the entity with ID %d.', $field_name, $destination->id()));
+      try {
+        $destination_field_list = $destination->get($field_name);
+      }
+      catch (\Exception $e) {
+        throw new SerializableHttpException(400, sprintf('The provided field (%s) does not exist in the entity with ID %s.', $field_name, $destination->uuid()));
       }
 
       $origin_field_list = $origin->get($field_name);
@@ -675,9 +678,7 @@ class EntityResource {
    *   Returns TRUE, if entity field is EntityReferenceItem.
    */
   protected function isRelationshipField($entity_field) {
-    /** @var \Drupal\Core\Field\FieldTypePluginManager $field_type_manager */
-    $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
-    $class = $field_type_manager->getPluginClass($entity_field->getDataDefinition()->getType());
+    $class = $this->pluginManager->getPluginClass($entity_field->getDataDefinition()->getType());
     return ($class == EntityReferenceItem::class || is_subclass_of($class, EntityReferenceItem::class));
   }
 
diff --git a/src/Controller/RequestHandler.php b/src/Controller/RequestHandler.php
index c4616b0..1231e18 100644
--- a/src/Controller/RequestHandler.php
+++ b/src/Controller/RequestHandler.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\jsonapi\Controller;
 
+use Drupal\Component\Serialization\Json;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Render\RenderContext;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -165,6 +166,8 @@ class RequestHandler implements ContainerAwareInterface, ContainerInjectionInter
     $response->addCacheableDependency($this->container->get('config.factory')
       ->get('jsonapi.resource_info'));
 
+    assert('$this->validateResponse($response)', 'A JSON API response failed validation (see /admin/reports/dblog for details). Please report this in the issue queue on drupal.org');
+
     return $response;
   }
 
@@ -286,4 +289,41 @@ class RequestHandler implements ContainerAwareInterface, ContainerInjectionInter
     return $resource;
   }
 
+  /**
+   * Validates a response against the JSON API specification.
+   *
+   * Only responses that are in the api_json format will be validated.
+   *
+   * @param \Drupal\jsonapi\ResourceResponse $response
+   *   The response to validate.
+   *
+   * @return bool
+   *   FALSE if the response failed validation, otherwise TRUE.
+   */
+  protected static function validateResponse(ResourceResponse $response) {
+    if (!class_exists("\\JsonSchema\\Validator")) {
+      return TRUE;
+    }
+    $response_data = json_decode($response->getContent());
+    if (empty($response_data)) {
+      return TRUE;
+    }
+
+    $validator = new \JsonSchema\Validator;
+    $schema_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'jsonapi') . '/schema.json';
+
+    $validator->check($response_data, (object)['$ref' => 'file://' . $schema_path]);
+
+    if (!$validator->isValid()) {
+      \Drupal::logger('jsonapi')->debug('Response failed validation: @data', [
+        '@data' => Json::encode($response_data),
+      ]);
+      \Drupal::logger('jsonapi')->debug('Validation errors: @errors', [
+        '@errors' => Json::encode($validator->getErrors()),
+      ]);
+    }
+
+    return $validator->isValid();
+  }
+
 }
diff --git a/tests/src/Functional/JsonApiFunctionalTest.php b/tests/src/Functional/JsonApiFunctionalTest.php
index 87fdadb..410c5a8 100644
--- a/tests/src/Functional/JsonApiFunctionalTest.php
+++ b/tests/src/Functional/JsonApiFunctionalTest.php
@@ -9,6 +9,7 @@ use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
 use Drupal\file\Entity\File;
+use Drupal\jsonapi\ResourceResponse;
 use Drupal\jsonapi\Routing\Param\OffsetPage;
 use Drupal\taxonomy\Entity\Term;
 use Drupal\taxonomy\Entity\Vocabulary;
@@ -668,6 +669,25 @@ class JsonApiFunctionalTest extends BrowserTestBase {
     $this->assertEquals("body.0.format: The value you selected is not a valid choice.", $updated_response['errors'][1]['detail']);
     $this->assertEquals("/data/attributes/title", $updated_response['errors'][0]['source']['pointer']);
     $this->assertEquals("/data/attributes/body/format", $updated_response['errors'][1]['source']['pointer']);
+    // 13. PATCH with field that doesn't exist on Entity.
+    $body = [
+      'data' => [
+        'id' => $uuid,
+        'type' => 'node--article',
+        'attributes' => [
+          'field_that_doesnt_exist' => 'foobar',
+        ],
+      ],
+    ];
+    $response = $this->request('PATCH', $individual_url, [
+      'body' => Json::encode($body),
+      'auth' => [$this->user->getUsername(), $this->user->pass_raw],
+      'headers' => ['Content-Type' => 'application/vnd.api+json'],
+    ]);
+    $updated_response = Json::decode($response->getBody()->__toString());
+    $this->assertEquals(400, $response->getStatusCode());
+    $this->assertEquals("The provided field (field_that_doesnt_exist) does not exist in the entity with ID $uuid.",
+      $updated_response['errors']['0']['detail']);
     // 13. Successful DELETE.
     $response = $this->request('DELETE', $individual_url, [
       'auth' => [$this->user->getUsername(), $this->user->pass_raw],
@@ -746,4 +766,53 @@ class JsonApiFunctionalTest extends BrowserTestBase {
     }
   }
 
+  public function testResponseValidation() {
+    // Expose the protected RequestHandler::validateResponse() method.
+    $class = new \ReflectionClass('Drupal\jsonapi\Controller\RequestHandler');
+    $validate_response = $class->getMethod('validateResponse');
+    $validate_response->setAccessible(TRUE);
+
+    // Test validation failure: no "type" in "data".
+    $json = <<<'EOD'
+{
+  "data": {
+    "id": "4f342419-e668-4b76-9f87-7ce20c436169",
+    "attributes": {
+      "nid": "1",
+      "uuid": "4f342419-e668-4b76-9f87-7ce20c436169"
+    }
+  }
+}
+EOD;
+    $response = new ResourceResponse();
+    $response->setContent($json);
+    if ($validate_response->invoke(NULL, $response)) {
+      $this->fail('Response validation failed to flag an invalid response.');
+    }
+
+    // Test validation success.
+    $json = <<<'EOD'
+{
+  "data": {
+    "type": "node--article",
+    "id": "4f342419-e668-4b76-9f87-7ce20c436169",
+    "attributes": {
+      "nid": "1",
+      "uuid": "4f342419-e668-4b76-9f87-7ce20c436169"
+    }
+  }
+}
+EOD;
+    $response->setContent($json);
+    if (!$validate_response->invoke(NULL, $response)) {
+      $this->fail('Response validation flagged a valid response.');
+    }
+
+    // Test validation of an empty response passes.
+    $response = new ResourceResponse();
+    if (!$validate_response->invoke(NULL, $response)) {
+      $this->fail('Response validation flagged a valid empty response.');
+    }
+  }
+
 }
diff --git a/tests/src/Unit/Access/CustomParameterNamesTest.php b/tests/src/Unit/Access/CustomParameterNamesTest.php
index 632b402..130ae77 100644
--- a/tests/src/Unit/Access/CustomParameterNamesTest.php
+++ b/tests/src/Unit/Access/CustomParameterNamesTest.php
@@ -32,7 +32,7 @@ class CustomParameterNamesTest extends \PHPUnit_Framework_TestCase {
   }
 
   public function providerTestJsonApiParamsValidation() {
-    // Copied from http://jsonapi.org/format/upcoming/#document-member-names.
+    // Copied from http://jsonapi.org/format/#document-member-names.
     $data = [];
     $data['alphanumeric-lowercase'] = ['12kittens', TRUE];
     $data['alphanumeric-uppercase'] = ['12KITTENS', TRUE];
