If an entity representation (JSON-LD, XML etc.) is received and deserialized to an entity object we should make sure that the fields on the entity are valid. This has to be done on write operations that create or update on entity ==> POST, PATCH and PUT in our case.

Example: A node title must not be longer than 256 characters. If a node is submitted with a title length of 257 characters the node should not be saved and a HTTP response with status 400 should be returned.

Before we can start to implement this we need a working entity validation system in place. Issues:

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

effulgentsia’s picture

Priority: Normal » Major
klausi’s picture

Status: Active » Needs review
FileSize
2.03 KB

Here is a first patch that adds the validate() invocation to the entity resource plugin.

TODO: test case that actually triggers a constraint violation (e.g. for nodes), so this depends on #2002156: Create validation constraints on nodes to entity validation.

moshe weitzman’s picture

Issue tags: +Needs tests

Add tag.

moshe weitzman’s picture

Status: Needs review » Needs work
klausi’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
2.56 KB
4.61 KB

Actually we can use a test for an over long UUID to trigger one example validation error response. Note that we only want to test an example here, the rest should be tested with explicit entity validation API tests for every entity (as we are doing already in the various issues outlined in #1696648: [META] Untie content entity validation from form validation.

Berdir’s picture

Doesn't need to be here but is it possible to provide a separate validate possibility? There are many use cases where you just want to check if the object you have (maybe a user is changing it) is still valid, without actually submitting it.

klausi’s picture

Interesting idea. No, that is currently not planned nor supported by core's REST module. Not sure how that could be implemented - maybe with a URL request parameter like POST /entity/node/1?validate_only=1 ?

Berdir’s picture

Tried to google this a bit, what I found is http://restfulobjects.org/download/. They define validation like this:

If validation logic has been defined for a property value, a collection
reference, or an action’s parameter(s), then the server implementation is
expected to perform that validation prior to initiating any change. For
example, a Customer’s firstName property might disallow certain
characters , or its showPayments() action might require that the toDate
parameter is greater than the fromDate.
A validation failure will generate a 422 "unprocessable entity" status code,
and in addition, a warning message will be returned. This will either be a
simple Warning header, or, dependent on the request, may be part of the
response, in the form of an"invalidReason" json-property.

x-ro-validate-only reserved query parameter

On occasion a client may want to validate one or more property fields,
before attempting to modify an object, or may want to validate
arguments before attempting to invoke the action.
Restful Objects defines an optional capability §B8 whereby the client can
set the reserved x–ro-validate-only query param for the request to
indicate that only validation should be performed:

If the validation completes, then a 204 "No content " status code will be
returned. If a validation failure occurs, then the response will be 422
"unprocessable entity" with corresponding Warning header /
"invalidReason" json-properties.

That x-ro-validate-only argument is not a GET argument, but is part of the body payload, older versions of that spec also used a HTTP header I think. Not sure if part of the payload is possible for us, but mixing POST and GET arguments sounds like a bad idea :)

klausi’s picture

Mixing the serialized body with request metadata is bad idea, too, we are not going to do that.

You could also use GET for the validation operation, since it does not change any data. It is just a bit uncommon to send a request body along with a GET request, but I think it does not violate the HTTP spec.
Or you could use a custom HTTP header like X-Validate-Only.

422 instead of 400 as response code on validation errors sounds interesting, looks like it is part of WebDAV; RFC 4918. Should we use that?

corvus_ch’s picture

Mixing the serialized body with request metadata is bad idea, too, we are not going to do that.

I consider it quite valid to use a data transfer object in a service that also holds metadata.

You could also use GET for the validation operation, since it does not change any data. It is just a bit uncommon to send a request body along with a GET request, […]

Since we have guzzle as part of core, no we can't. Guzzle does not support to add body data in a GET request. See https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/Message/Req... and https://github.com/guzzle/guzzle/blob/master/src/Guzzle/Http/Message/Req....

Using the query string certainly will exceed the limit of the url length. And it will be a data leak when using https.

[…] but I think it does not violate the HTTP spec.

The HTTP standard is unclear about this. The result will be at least unpredictable. See http://redmine.lighttpd.net/boards/2/topics/3346. I am not sure if Lighttpd still does not allow a message body in a GET request but there are others (see Guzzle).

Or you could use a custom HTTP header like X-Validate-Only.

That certainly is an option. I am not sure if I really like it or not.

Personally I would prefer to go with the suggestion of RestfullObjects or at least something similar. We already have meta data in the body coming from HAL. I feel we could really profit from adding metadata as an embedded resource. There certainly will come other use cases this will come in handy. Using it for validation is just one use case I can see and it is the approach that leaves room for other use cases.

422 instead of 400 as response code on validation errors sounds interesting, looks like it is part of WebDAV; RFC 4918. Should we use that?

Sure. We should get the maximum out of the HTTP standard. Using all available response codes were ever they makes sense will make things a lot easier for error handing.

klausi’s picture

In REST the body contains a representation of a resource and nothing else. Sure, the data contains references and hypermedia controls pointing to other resources, but it never contains metadata about the current request.

The use case of validation only sounds like you would use a separate resource for that: POST to /entity/node/validation or PATCH to /entity/node/1/validation feels like a better fit.

I agree that query parameters on POST and a body on GET requests are also wrong, so the alternative would be to use a custom HTTP header or a custom media type used by an Accept header like application/vnd.drupal.validation+json.

corvus_ch’s picture

In REST the body contains a representation of a resource and nothing else. Sure, the data contains references and hypermedia controls pointing to other resources, but it never contains metadata about the current request.

What is in the body and what not is the freedom of the service architect. Working directly with the entity is fine. Using a DTO is too. Think about attributes you do not want to expose for security reasons or because they are calculated based on some business rules. Or it may also be that the entity you expose through a service does not exist on the server but is built up from several ones. I agree that for Drupal normally this should not be the case.

The use case of validation only sounds like you would use a separate resource for that: POST to /entity/node/validation or PATCH to /entity/node/1/validation feels like a better fit.

I can live with that.

[…] alternative would be to use a custom HTTP header

That I also can live with.

or a custom media type used by an Accept header like application/vnd.drupal.validation+json.

I disagree. By using Accept you tell the server what you would like to have as a response format. There is no guarantee that you will get it and it is also not meant to tell the server to change its behavior.

If I must choose between HTTP header and validation resource, I would choose the resource.

klausi’s picture

Putting the (interesting) validation resource discussion aside, please open a separate issue for that.

Here is a rerolled patch that uses 422 in the validation error case.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

Looks good to me. If we reroll this, I think we could remove the text ""Unprocessable Entity". I think "Entity validation failed" works better.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed dc3ba2b and pushed to 8.x. Thanks!

re #14 "422 Unprocessable Entity" is a http status message see https://en.wikipedia.org/wiki/List_of_HTTP_status_codes so looks good to me.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

Anonymous’s picture

Issue summary: View changes

added issue