Json validation error list

Handling Validation Errors# When an invalid instance is encountered, a ValidationError will be raised or returned, depending on which method or function is used. exception jsonschema.exceptions. ValidationError ( message , validator= , path=() , cause=None , context=() , validator_value= , instance= , schema= , schema_path=() , parent=None , type_checker= ) [source] An instance was […]

Содержание

  1. Handling Validation Errors#
  2. ErrorTrees#
  3. best_match and relevance#
  4. Structuring validation errors in REST APIs
  5. Basic example
  6. Complex data structures validation
  7. Multiple errors per field
  8. Error details
  9. Missing and redundant fields, non-JSON request body
  10. Request body, URL and query parameters validation
  11. Summary

Handling Validation Errors#

When an invalid instance is encountered, a ValidationError will be raised or returned, depending on which method or function is used.

exception jsonschema.exceptions. ValidationError ( message , validator= , path=() , cause=None , context=() , validator_value= , instance= , schema= , schema_path=() , parent=None , type_checker= ) [source]

An instance was invalid under a provided schema.

The information carried by an error roughly breaks down into:

Why Did It Happen

What Was Being Validated

A human readable message explaining the error.

The name of the failed keyword.

The associated value for the failed keyword in the schema.

The full schema that this error came from. This is potentially a subschema from within the schema that was passed in originally, or even an entirely different schema if a $ref was followed.

A collections.deque containing the path to the failed keyword within the schema.

A collections.deque containing the path to the failed keyword within the schema, but always relative to the original schema as opposed to any subschema (i.e. the one originally passed into a validator class, not schema ).

A collections.deque containing the path to the offending element within the instance. The deque can be empty if the error happened at the root of the instance.

A collections.deque containing the path to the offending element within the instance. The absolute path is always relative to the original instance that was validated (i.e. the one passed into a validation method, not instance ). The deque can be empty if the error happened at the root of the instance.

A JSON path to the offending element within the instance.

The instance that was being validated. This will differ from the instance originally passed into validate if the validator object was in the process of validating a (possibly nested) element within the top-level instance. The path within the top-level instance (i.e. ValidationError.path ) could be used to find this object, but it is provided for convenience.

If the error was caused by errors in subschemas, the list of errors from the subschemas will be available on this property. The schema_path and path of these errors will be relative to the parent error.

If the error was caused by a non-validation error, the exception object will be here. Currently this is only used for the exception raised by a failed format checker in jsonschema.FormatChecker.check .

A validation error which this error is the context of. None if there wasn’t one.

In case an invalid schema itself is encountered, a SchemaError is raised.

exception jsonschema.exceptions. SchemaError ( message , validator= , path=() , cause=None , context=() , validator_value= , instance= , schema= , schema_path=() , parent=None , type_checker= ) [source]

A schema was invalid under its corresponding metaschema.

The same attributes are present as for ValidationError s.

These attributes can be clarified with a short example:

The error messages in this situation are not very helpful on their own.

If we look at ValidationError.path on each of the errors, we can find out which elements in the instance correspond to each of the errors. In this example, ValidationError.path will have only one element, which will be the index in our list.

Since our schema contained nested subschemas, it can be helpful to look at the specific part of the instance and subschema that caused each of the errors. This can be seen with the ValidationError.instance and ValidationError.schema attributes.

With keywords like anyOf, the ValidationError.context attribute can be used to see the sub-errors which caused the failure. Since these errors actually came from two separate subschemas, it can be helpful to look at the ValidationError.schema_path attribute as well to see where exactly in the schema each of these errors come from. In the case of sub-errors from the ValidationError.context attribute, this path will be relative to the ValidationError.schema_path of the parent error.

The string representation of an error combines some of these attributes for easier debugging.

ErrorTrees#

If you want to programmatically query which validation keywords failed when validating a given instance, you may want to do so using jsonschema.exceptions.ErrorTree objects.

class jsonschema.exceptions. ErrorTree ( errors = () ) [source]

ErrorTrees make it easier to check which validations failed.

The mapping of validator keywords to the error objects (usually jsonschema.exceptions.ValidationError s) at this level of the tree.

Check whether instance[index] has any errors.

Retrieve the child tree one level down at the given index .

If the index is not in the instance that this tree corresponds to and is not known by this tree, whatever error would be raised by instance.__getitem__ will be propagated (usually this is some subclass of LookupError .

__init__ ( errors = () ) [source] __iter__ ( ) [source]

Iterate (non-recursively) over the indices in the instance with errors.

__setitem__ ( index , value ) [source]

Add an error to the tree at the given index .

The total number of errors in the entire tree, including children.

Consider the following example:

For clarity’s sake, the given instance has three errors under this schema:

Let’s construct an jsonschema.exceptions.ErrorTree so that we can query the errors a bit more easily than by just iterating over the error objects.

As you can see, jsonschema.exceptions.ErrorTree takes an iterable of ValidationError s when constructing a tree so you can directly pass it the return value of a validator object’s jsonschema.protocols.Validator.iter_errors method.

ErrorTree s support a number of useful operations. The first one we might want to perform is to check whether a given element in our instance failed validation. We do so using the in operator:

The interpretation here is that the 0th index into the instance ( «spam» ) did have an error (in fact it had 2), while the 1th index ( 2 ) did not (i.e. it was valid).

If we want to see which errors a child had, we index into the tree and look at the ErrorTree.errors attribute.

Here we see that the enum and type keywords failed for index 0 . In fact ErrorTree.errors is a dict, whose values are the ValidationError s, so we can get at those directly if we want them.

Of course this means that if we want to know if a given validation keyword failed for a given index, we check for its presence in ErrorTree.errors :

Finally, if you were paying close enough attention, you’ll notice that we haven’t seen our minItems error appear anywhere yet. This is because minItems is an error that applies globally to the instance itself. So it appears in the root node of the tree.

That’s all you need to know to use error trees.

To summarize, each tree contains child trees that can be accessed by indexing the tree to get the corresponding child tree for a given index into the instance. Each tree and child has a ErrorTree.errors attribute, a dict, that maps the failed validation keyword to the corresponding validation error.

best_match and relevance#

The best_match function is a simple but useful function for attempting to guess the most relevant error in a given bunch.

Try to find an error that appears to be the best match among given errors.

In general, errors that are higher up in the instance (i.e. for which ValidationError.path is shorter) are considered better matches, since they indicate “more” is wrong with the instance.

If the resulting match is either oneOf or anyOf, the opposite assumption is made – i.e. the deepest error is picked, since these keywords only need to match once, and any other errors may not be relevant.

errors (collections.abc.Iterable) – the errors to select from. Do not provide a mixture of errors from different validation attempts (i.e. from different instances or schemas), since it won’t produce sensical output.

key (collections.abc.Callable) – the key to use when sorting errors. See relevance and transitively by_relevance for more details (the default is to sort with the defaults of that function). Changing the default is only useful if you want to change the function that rates errors but still want the error context descent done by this function.

the best matching error, or None if the iterable was empty

This function is a heuristic. Its return value may change for a given set of inputs from version to version if better heuristics are added.

A key function that sorts errors based on heuristic relevance.

If you want to sort a bunch of errors entirely, you can use this function to do so. Using this function as a key to e.g. sorted or max will cause more relevant errors to be considered greater than less relevant ones.

Within the different validation keywords that can fail, this function considers anyOf and oneOf to be weak validation errors, and will sort them lower than other errors at the same level in the instance.

If you want to change the set of weak [or strong] validation keywords you can create a custom version of this function with by_relevance and provide a different set of each.

Create a key function that can be used to sort errors by relevance.

weak (set) – a collection of validation keywords to consider to be “weak”. If there are two errors at the same level of the instance and one is in the set of weak validation keywords, the other error will take priority. By default, anyOf and oneOf are considered weak keywords and will be superseded by other same-level validation errors.

strong (set) – a collection of validation keywords to consider to be “strong”

Источник

Structuring validation errors in REST APIs

JSON REST API over HTTP is probably the most common method for integrating mobile, web, TV, (and more) applications with backend systems. It is also widely used for server-to-server communication. Yet, when I was looking for some articles about structuring validation errors in REST APIs I haven’t found much. And most of the described solutions were not satisfying.

As a result, I decided to design format that will fulfil my needs. Requirements emerged when I was working on two completely different projects. Thanks to that, they should be generic enough to fit into the majority of use cases. The requirements that I gathered were following:

  • It should allow to aggregate multiple errors that occurred. Instead of returning first one.
  • It should allow a client to link validation errors with invalid fields in complex data structures. Like nested lists and dictionaries.
  • It should give client full control over presentation. By returning errors in machine-readable format.
  • It should be JSON serializable

Basic example

Let’s start with a simple example. Assume that our API expects request body in JSON format. It should be a dictionary with two fields: username and password . In API we want to validate if request body doesn’t have empty name or password . When a client sends such request body:

Our API should return validation errors formatted as follows:

As you can see the generic structure is a list of objects, where every element describes the single error. It allows to aggregate multiple validation errors. Each object has two mandatory fields. error which contains a name of an error in machine readable format. And path which references field where an error occurred.

API gives client full control over presentation, because error has machine readable format. It doesn’t impose already translated message or message format. It also gives client all necessary data to tell user what happened ( error ) and where ( path )

Complex data structures validation

One purpose of this format is to allow to aggregate multiple validation errors from complex data structures. path property allows to reference any field. No matter how deeply it lives inside data structure. It contains all object properties and array indexes that leads to given item.

For example. If we want to reference name field inside second array element in the following JSON:

We need to use following path: [«users», 1, «name»] . First element references users field in top-level object. Next is index of array item (zero-indexed) 1 . Finally we have name field.

Let’s see how it works with validation errors. Assume that we send following JSON to API in request body:

API requires that username field can not be blank. In that case we should get following validation errors:

What if we want to put constraint on array, such as maximum number of items? Then path should reference array itself instead of array specific elements. For example

Multiple errors per field

It isn’t rare case when single field has multiple constraints attached. For example, assume that username field can’t be longer than 32 characters and must not contain any vulgarisms. If request sent by client breaches both constraints API could return multiple errors for the same path:

Error details

This format is easily extensible if we want to provide more details for given error. Thanks that client application has more data to produce more detailed error message. For example, assume that our API expects that password is at least 6 characters long. When client sends too short password, API can return following error:

Thanks that client can render error message like “Password must be at least 6 characters long”. It’s crucial that every error type should always have the same set of details fields. For example, tooShort error should always be accompanied by minLength field with no exceptions. Without such constraint, API responses will be much harder to handle by clients.

Missing and redundant fields, non-JSON request body

There are few special cases that may require additional explanation.

Users rarely communicate directly with API. Instead they are using client applications. So, it’s client application responsibility to send exactly such data structure that API is expecting. Otherwise, even if user will receive error message about missing / redundant fields he won’t be able to do anything with it. However, detailed validation errors could still be pretty helpful for client application developers. They’ll see which fields are missing or redundant.

Let’s see an example. Assume that API requires single username field in the request body. However, a client sends following request:

In that case, API should return following error:

Last special case is when our API can not decode request that it received (e.g. because it is not valid JSON). This case deserves separate error type. It should be distinguished from validation errors described above. If you are using “Problem Details” standard described in RFC 7807 then you can use different type or not parsable JSON request:

While, for validation errors you could use following envelope

Request body, URL and query parameters validation

So far I focused on HTTP request body validation. But nothing stands in the way to use it also for validation of other parts of the request. Such as path or query parameters. To achieve that we can prepend path of every validation error with the name of part of the request.
In most cases, we’ll validate request URL, body and query parameters. So, we can distinguish following prefixes: $query , $body and $url .

URL part we’ll usually treat as a list. So path parameter will contain an index of invalid URL segment.

For example, let’s assume that we have following URL template: /question// . It expects that id and userId are integers. It also allows for extra query parameter: direction . And it can have one of two values: ascending or descending

For invalid request like /question/10/def?direction=foo

API should return following validation errors:

As you can second invalid URL segment ( userId ) is referenced by path: [«$url», 1] . And invalid query parameter is referenced by [«$query», «direction»] .

Summary

I hope that you find this solution simple and elegant. But, there is one more advantage of this format. You should be able to easily implement it by taking advantage of existing validation libraries. So far, I’ve done it in two distinct projects. First was written in Python, second in PHP. In next article, I would like to describe how to move this concept into actual implementation.

Источник

  • Introduction
  • Error Objects
  • Error Lists
  • Responses
    • HTTP Status
  • JSON:API Exceptions
    • Helper Methods
  • Validation Errors
    • Source Pointers
  • Error Rendering
    • JSON Responses
    • Middleware Responses
    • Always Rendering JSON:API Errors
    • Custom Rendering Logic
    • Converting Exceptions
  • Error Reporting

# Introduction

The JSON:API specification defines error objects (opens new window)
that are used to provide information to a client about problems encountered
while performing an operation.

Errors are returned to the client in the top-level errors member of the
JSON document.

Laravel JSON:API makes it easy to return errors to the client — either
as responses, or by throwing exceptions. In addition, the exception
renderer you added to your exception handler during
installation takes care of
converting standard Laravel exceptions to JSON:API error responses
if the client has sent an Accept: application/vnd.api+json header.

# Error Objects

Use our LaravelJsonApiCoreDocumentError object to create a JSON:API
error object. The easiest way to construct an error object is using the
static fromArray method. For example:

The fromArray method accepts all the error object members
defined in the specification. (opens new window)

Alternatively, if you want to use setters, use the static make method
to fluently construct your error object:

The available setters are:

  • setAboutLink
  • setCode
  • setDetail
  • setId
  • setLinks
  • setMeta
  • setStatus
  • setSource
  • setSourceParameter
  • setSourcePointer
  • setTitle

# Error Lists

If you need to return multiple errors at once, use our
LaravelJsonApiCoreDocumentErrorList class. This accepts any number
of error objects to its constructor. For example:

Use the push method to add errors after constructing the error list:

# Responses

Both the Error and ErrorList classes implement Laravel’s Responsable
interface. This means you can return them directly from controller actions
and they will be converted to a JSON:API error response.

If you need to customise the error response, then you need to use our
LaravelJsonApiCoreResponsesErrorResponse class. Either create
a new one, passing in the Error or ErrorList object:

Or alternatively, use the prepareResponse method on either the Error
or ErrorList object:

The ErrorResponse class has all the helper methods
required to customise both the headers and the JSON:API document that
is returned in the response.

For example, if we were adding a header and meta to our response:

# HTTP Status

The JSON:API specification says:

When a server encounters multiple problems for a single request,
the most generally applicable HTTP error code SHOULD be used in the response.
For instance, 400 Bad Request might be appropriate for multiple 4xx errors
or 500 Internal Server Error might be appropriate for multiple 5xx errors.

Our ErrorResponse class takes care of calculating the HTTP status for you.

If there is only one error, or all the errors have the same status, then
the response status will match this status.

If you have multiple errors with different statuses, the response status
will be 400 Bad Request if the error objects only have 4xx status codes.
If there are any 5xx status codes, the response status will be
500 Internal Server Error.

If the response has no error objects, or none of the error objects have a
status, then the response will have a 500 Internal Server Error status.

If you want the response to have a specific HTTP status, use the
withStatus method. For example:

# JSON:API Exceptions

Our LaravelJsonApiCoreExceptionsJsonApiException allows you to terminate
processing of a request by throwing an exception with JSON:API errors
attached.

The exception expects its first argument to be either an Error or an
ErrorList object. For example:

The JsonApiException class has all the helper methods
required to customise both the headers and the JSON:API document that
is returned in the response. Use the static make method if you need to
call any of these methods. For example:

There is also a handy static error method. This allows you to fluently
construct an exception for a single error, providing either an Error
object or an array. For example:

# Helper Methods

The JsonApiException class has a number of helper methods:

  • is4xx
  • is5xx
  • getErrors

# is4xx

Returns true if the HTTP status code is a client error, i.e. in the 400-499
range.

# is5xx

Returns true if the HTTP status code is a server error, i.e. in the 500-599
range.

# getErrors

Use the getErrors() method to retrieve the JSON:API error objects from the
exception. For example, if we wanted to log the errors:

# Validation Errors

Our implementation of resource requests and
query parameter requests already takes
care of converting Laravel validation error messages to JSON:API errors.

If however you have a scenario where you want to convert a failed validator
to JSON:API errors manually, we provide the ability to do this.

You will need to resolve an instance of LaravelJsonApiValidationFactory
out of the service container. For example, you could use the app helper,
or use dependency injection by type-hinting it in a constructor of a service.

Once you have the factory instance, use the createErrors method,
providing it with the validator instance. For example, in a controller
action:

The object this returns is Responsable — so you can return it directly
from a controller action. If you want to convert it to an error response,
use our prepareResponse pattern as follows:

# Source Pointers

By default this process will convert validation error keys to JSON source
pointers. For example, if you have a failed message for the foo.bar
value, the resulting error object will have a source pointer of
/foo/bar.

If you need to prefix the pointer value, use the withSourcePrefix method.
The following example would convert foo.bar to /data/attributes/foo/bar:

If you need to fully customise how the validation key should be converted,
provide a Closure to the withPointers method:

# Error Rendering

As described in the installation instructions,
the following should have been added to the register method on your
application’s exception handler:

The Laravel exception handler already takes care of converting exceptions to
either application/json or text/html responses. Our exception handler
effectively adds JSON:API error responses as a third media type. If the
client has sent a request with an Accept header of application/vnd.api+json,
then they will receive the exception response as a JSON:API error response —
even if the endpoint they are hitting is not one of your JSON:API server
endpoints.

WARNING

There are some scenarios where the Laravel exception handler does not call
any registered renderables. For example, if an exception implements Laravel’s
Responsable interface, our exception parser will not be invoked as the handler
uses the Responsable::toResponse() method on the exception to generate a
response.

# JSON Responses

If a client encounters an exception when using an Accept header of
application/json, they will still receive Laravel’s default JSON exception
response, rather than a JSON:API response.

If you want our exception parser to render JSON exception responses instead
of the default Laravel response, use the acceptsJson() method when registering
our exception parser:

# Middleware Responses

Sometimes you may want exceptions to be converted to JSON:API errors if the
current route has a particular middleware applied to it. The most common example
of this would be if you want JSON:API errors to always be rendered if the
current route has the api middleware.

In this scenario, use the acceptsMiddleware() method when registering our
exception parser. For example:

TIP

You can provide multiple middleware names to the acceptsMiddleware() method.
When you do this, it will match a route that contains any of the provided
middleware.

# Always Rendering JSON:API Errors

If you want our exception parser to always convert exceptions to JSON:API
errors, use the acceptsAll() helper method:

# Custom Rendering Logic

If you want your own logic for when a JSON:API exception response should be
rendered, pass a closure to the accept() method.

For example, let’s say we wanted our API to always return JSON:API exception
responses, regardless of what Accept header the client sent. We would use
the request is() method to check if the path is our API:

TIP

If you return false from the callback, the normal exception rendering logic
will run — meaning a client that has sent an Accept header with the JSON:API
media type will still receive a JSON:API response. This is semantically correct,
as the Accept header value should be respected.

# Converting Exceptions

Our exception parser is built so that you can easily add support for
custom exceptions to the JSON:API rendering process. The implementation works
using a pipeline, meaning you can add your own handlers for converting
exceptions to JSON:API errors.

For example, imagine our application had a PaymentFailed exception, that
we wanted to convert to JSON:API errors if thrown to the exception handler.
We would write the following class:

We can then add it to the JSON:API exception parser using either the
prepend or append method:

# Error Reporting

As described in the installation instructions,
the following should have been added to the $dontReport property on your
application’s exception handler:

This prevents our JsonApiException from being reported in your application’s
error log. This is a sensible starting position, as the JsonApiException
class is effectively a HTTP exception that needs to be rendered to the client.

However, this does mean that any JsonApiException that has a 5xx status
code (server-side error) will not be reported in your error log. Therefore,
an alternative is to use the helper methods on the JsonApiException class
to determine whether or not the exception should be reported.

To do this, we will use the reportable() method to register a callback
for the JSON:API exception class. (At the same time, we remove the exception
class from the $dontReport property.) For example, the following will stop
the propagation of JSON:API exceptions to the default logging stack if the
exception does not have a 5xx status:

In the following example, we log 4xx statuses as debug information, while
letting all other JSON:API exceptions propagate to the default logging stack:

With a Subscription, click any sentence in the script to jump to that part of the video!

Login
Subscribe

Our first goal is to read the JSON validation errors and add them visually to the form. A moment ago, when I filled out the form with no rep number, the endpoint sent back an error structure that looked like this: with an errors key and a key-value array of errors below that.

Parsing the Error JSON

To get this data, we need to parse the JSON manually with var errorData = JSON.parse(jqXHR.responseText):


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 61
handleNewFormSubmit: function(e) {

… lines 63 — 70
$.ajax({

… lines 72 — 78
error: function(jqXHR) {
var errorData = JSON.parse(jqXHR.responseText);

… line 81
}
});
},

… lines 85 — 88
});

… lines 90 — 107
})(window, jQuery);

That’s the raw JSON that’s sent back from the server.

To actually map the errorData onto our fields, let’s create a new function below called _mapErrorsToForm with an errorData argument. To start, just log that:


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 85
_mapErrorsToForm: function(errorData) {
console.log(errorData);
}
});

… lines 90 — 107
})(window, jQuery);

Above, to call this, we know we can’t use this because we’re in a callback. So add the classic var self = this;, and then call self._mapErrorsToForm(errorData.errors):


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 61
handleNewFormSubmit: function(e) {

… lines 63 — 69
var self = this;
$.ajax({

… lines 72 — 78
error: function(jqXHR) {
var errorData = JSON.parse(jqXHR.responseText);
self._mapErrorsToForm(errorData.errors);
}
});
},

… lines 85 — 88
});

… lines 90 — 107
})(window, jQuery);

All the important stuff is under the errors key, so we’ll pass just that.

Ok, refresh that! Leave the form empty, and submit! Hey, beautiful error data!

Mapping Data into HTML

So how can we use this data to make actual HTML changes to the form? There are generally two different approaches. First, the simple way: parse the data by hand and manually use jQuery to add the necessary elements and classes. This is quick to do, but doesn’t scale when things get really complex. The second way is to use a client-side template. We’ll do the simple way first, but then use a client-side template for a more complex problem later.

And actually, there’s a third way: which is to use a full front-end framework like ReactJS. We’ll save that for a future tutorial.

Creating a Selectors Map

In _mapErrorsToForm, let’s look at the error data and use it to add an error span below that field. Obviously, we need to use jQuery to find our .js-new-rep-log-form form element.

But wait! Way up in our constructor, we’re already referencing this selector:


… lines 1 — 2
(function(window, $) {
window.RepLogApp = function ($wrapper) {

… lines 5 — 17
this.$wrapper.on(

… line 19
‘.js-new-rep-log-form’,

… line 21
);
};

… lines 25 — 107
})(window, jQuery);

It’s no big deal, but I would like to not duplicate that class name in multiple places. Instead, add an _selectors property to our object. Give it a newRepForm key that’s set to its selector:


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {
_selectors: {
newRepForm: ‘.js-new-rep-log-form’
},

… lines 29 — 109
});

… lines 111 — 128
})(window, jQuery);

Now, reference that with this._selectors.newRepForm:


… lines 1 — 2
(function(window, $) {
window.RepLogApp = function ($wrapper) {

… lines 5 — 17
this.$wrapper.on(

… line 19
this._selectors.newRepForm,

… line 21
);
};

… lines 24 — 128
})(window, jQuery);

Below in our function, do the same: var $form = this.$wrapper.find(this._selectors.newRepForm):


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 89
_mapErrorsToForm: function(errorData) {

… line 91
var $form = this.$wrapper.find(this._selectors.newRepForm);

… lines 93 — 108
}
});

… lines 111 — 128
})(window, jQuery);

Mapping the Data Manually

Now what? Simple: loop over every field see if that field’s name is present in the errorData. And if it is, add an error message span element below the field. To find all the fields, use $form.find(':input') — that’s jQuery magic to find all form elements. Then, .each() and pass it a callback function:


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 89
_mapErrorsToForm: function(errorData) {

… line 91
var $form = this.$wrapper.find(this._selectors.newRepForm);

… lines 93 — 95
$form.find(‘:input’).each(function() {

… lines 97 — 107
});
}
});

… lines 111 — 128
})(window, jQuery);

Inside, we know that this is actually the form element. So we can say var fieldName = $(this).attr('name'):


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 89
_mapErrorsToForm: function(errorData) {

… lines 91 — 95
$form.find(‘:input’).each(function() {
var fieldName = $(this).attr(‘name’);

… lines 98 — 107
});
}
});

… lines 111 — 128
})(window, jQuery);

I’m also going to find the wrapper that’s around the entire form field. What I mean is, each field is surrounded by a .form-group element. Since we’re using Bootstrap, we also need to add a class to this. Find it with var $wrapper = $(this).closest('.form-group'):


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 89
_mapErrorsToForm: function(errorData) {

… lines 91 — 95
$form.find(‘:input’).each(function() {
var fieldName = $(this).attr(‘name’);
var $wrapper = $(this).closest(‘.form-group’);

… lines 99 — 107
});
}
});

… lines 111 — 128
})(window, jQuery);

Perfect!

Then, if there is not any data[fieldName], the field doesn’t have an error. Just continue:


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 89
_mapErrorsToForm: function(errorData) {

… lines 91 — 95
$form.find(‘:input’).each(function() {
var fieldName = $(this).attr(‘name’);
var $wrapper = $(this).closest(‘.form-group’);
if (!errorData[fieldName]) {
return;
}

… lines 103 — 107
});
}
});

… lines 111 — 128
})(window, jQuery);

If there is an error, we need to add some HTML to the page. The easy way to do that is by creating a new jQuery element. Set var $error to $() and then the HTML you want: a span with a js-field-error class and a help-block class:


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 89
_mapErrorsToForm: function(errorData) {

… lines 91 — 95
$form.find(‘:input’).each(function() {
var fieldName = $(this).attr(‘name’);
var $wrapper = $(this).closest(‘.form-group’);
if (!errorData[fieldName]) {
return;
}
var $error = $(‘<span class=»js-field-error help-block»></span>’);

… lines 105 — 107
});
}
});

… lines 111 — 128
})(window, jQuery);

I left the span blank because it’s cleaner to add the text on the next line: $error.html(errorsData[fieldName]):


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 89
_mapErrorsToForm: function(errorData) {

… lines 91 — 95
$form.find(‘:input’).each(function() {
var fieldName = $(this).attr(‘name’);
var $wrapper = $(this).closest(‘.form-group’);
if (!errorData[fieldName]) {
return;
}
var $error = $(‘<span class=»js-field-error help-block»></span>’);
$error.html(errorData[fieldName]);

… lines 106 — 107
});
}
});

… lines 111 — 128
})(window, jQuery);

This jQuery object is now done! But it’s not on the page yet. Add it with $wrapper.append($error). Also call $wrapper.addClass('has-error'):


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 89
_mapErrorsToForm: function(errorData) {

… lines 91 — 95
$form.find(‘:input’).each(function() {
var fieldName = $(this).attr(‘name’);
var $wrapper = $(this).closest(‘.form-group’);
if (!errorData[fieldName]) {
return;
}
var $error = $(‘<span class=»js-field-error help-block»></span>’);
$error.html(errorData[fieldName]);
$wrapper.append($error);
$wrapper.addClass(‘has-error’);
});
}
});

… lines 111 — 128
})(window, jQuery);

Yes! Let’s try it! Refresh and submit! There it is!

The only problem is that, once I finally fill in the field, the error message stays! AND, I get a second error message! Man, we gotta get this thing cleaned up!

No problem: at the top, use $form.find() to find all the .js-field-error elements. And, remove those. Next, find all the form-group elements and remove the has-error class:


… lines 1 — 2
(function(window, $) {

… lines 4 — 24
$.extend(window.RepLogApp.prototype, {

… lines 26 — 89
_mapErrorsToForm: function(errorData) {
var $form = this.$wrapper.find(this._selectors.newRepForm);
$form.find(‘.js-field-error’).remove();
$form.find(‘.form-group’).removeClass(‘has-error’);
$form.find(‘:input’).each(function() {

… lines 97 — 107
});
}
});

… lines 111 — 128
})(window, jQuery);

Refresh now, and re-submit! Errors! Fill in one… beautiful!

And if we fill in both fields, the AJAX call is successful, but nothing updates. Time to tackle that.

It didn’t take long for JSON to become the hottest thing since Pam Anderson slowly bounced her way down the BayWatch beaches. And why shouldn’t it be? JSON is easy to understand visually, easy to parse on both the client and server sides, and is supported in just about every language except aborigine. There is however one problem I see with the way JSON is used by developers today: lack of validation. Most developers assume the JSON provide is not only error-free also in the proper format. Bad assumption. Let me show you how Kris Zyp’s JSON Schema can help you validate JSON on both the client and server sides.

What is JSON Schema?

JSON Schema is a standard (currently in draft) which provides a coherent schema by which to validate a JSON «item» against. Properties within the schema are defined and with another object containing their expected type. For example:

"myObj" : {
	"type" : "array",
	"properties" : {
		"id": { "type": "number" },
		"username": { "type" : "string" }
	}
}

Besides providing the required type, other properties can be defined, including:

  • items: This should be a schema or an array of schemas. When this is an object/schema and the instance value is an array, all the items in the array must conform to this schema.
  • optional: Notes if the property should be considered optional
  • requires: This indicates that if this property is present in the containing instance object, the property given by requires attribute must also be present in the containing instance object.
  • maxItems: Defines the maximum number of items in the collection

Numerous other properties are available, all of which may be found at: http://tools.ietf.org/html/draft-zyp-json-schema-03

Defining a Simple JSON Schema

Let’s say that our application requires data in the following format:

{
	users: [
		{ id: 1, username: "davidwalsh", numPosts: 404, realName: "David Walsh" },
		{ id: 2, username: "russianprince", numPosts: 12, realName: "Andrei Arshavin" }
	]
}

Right away we can see:

  • The object has a users property
  • The users property is an array
  • The users array contains objects
  • Each object has an id (number), username (string), numPosts (number), and realName (string)

With this structure in mind, we can create a simple schema to validate our expected format:

{
	"type" : "object",
	"properties" : {
		"users" : {
			"type" : "array", // remember that arrays are objects
			"items" : { // "items" represents the items within the "users" array
				"type" : "object",
				"properties" : {
					"id": { "type": "number" },
					"username": { "type" : "string" },
					"numPosts": { "type" : "number" },
					"realName": { "type" : "string", optional: true }
				}
			}
		}
	}
}

dojox.json.schema and JSON Schema — Client Side

A JSON Schema validation routine is available with dojox.json.schema. The validate method accepts two arguments: your JSON to validate and the schema. Let’s load the schema we created above, along with the sample JSON we created, and validate it:

// Require the json scheme module
dojo.require("dojox.json.schema");

// When resources are ready
dojo.ready(function() {

	// Load the schema
	dojo.xhrGet({
		url: 'schema.json',
		handleAs: 'json',
		load: function(schema) {

			// Now load the JSON
			dojo.xhrGet({
				url: 'users.json',
				handleAs: 'json',
				load: function(users) {

					// Now validate it!
					var result = dojox.json.schema.validate(users,schema);

					// Show the result
					console.log(result);
				}
			});
		}
	});	
});

A true valid property signals that the JSON is valid. If the result fails validation, valid will be false and the errors property will contain an array of error messages detailing why the given property did not pass validation. Here’s a sample return result with errors:

{
	errors: [
		{
			message: "is missing and not optional",
			property: "users"
		}
	]
	valid: false
}

How you handle invalid data is up to you; moving forward with invalid data could present a security risk for both your organization and the user.

CommonJS-Utils and JSON Schema — Server Side

Kris also provides a server side JSON Schema validation routine within his CommonJS Utils project on GitHub. I’ve installed this project using NPM for NodeJS:

npm install commonjs-utils

Within this package is a json-schema resource. The following snippet requires that resources, reads in the schema and data JSON files, and validates the data JSON against the schema:

// Require Sys and FileSystem
var sys = require('sys'), fs = require('fs');

// Require package
var validate = require('commonjs-utils/json-schema').validate;

// Load a schema by which to validate
fs.readFile('schema.json',function(err,data) {
	if(err) throw err;
	var schema = data;
	// Load data file
	fs.readFile('./users.json',function(err,data) {
		if(err) throw err;
		// Parse as JSON
		var posts = JSON.parse(data);
		// Validate
		var validation = validate(posts, schema);
		// Echo to command line
		sys.puts('The result of the validation:  ',validation.valid);
	});
});

To run this via the command line:

node server-validate.js

The server side uses the exact same schema and data as the client side, so your web application can be covered on both fronts.

Closing Thoughts on JSON Schema

JSON Schema is still a draft but I think Kris has done an outstanding job in creating the draft and coding server and client side validators. JSON validation is often overlooked and the data is wrongly assumed as correct. The resources for data validation are available — it’s up to you to use them!

Website performance monitoring

Website performance monitoring

Recent Features

  • LightFace:  Facebook Lightbox for MooTools

    LightFace: Facebook Lightbox for MooTools

    One of the web components I’ve always loved has been Facebook’s modal dialog.  This «lightbox» isn’t like others:  no dark overlay, no obnoxious animating to size, and it doesn’t try to do «too much.»  With Facebook’s dialog in mind, I’ve created LightFace:  a Facebook lightbox…

  • Write Simple, Elegant and Maintainable Media Queries with Sass

Понравилась статья? Поделить с друзьями:
  • Js catch specific error
  • Js axios catch error code
  • Jrpcexception asn 1 decode error offset 0 unexpected end of buffer encountered что это
  • Jrpcexception asn 1 decode error offset 0 unexpected end of buffer encountered перевести
  • Jrexpressionevalexception error evaluating expression for source text