#42961 closed defect (bug) (fixed)
REST API: Cannot pass empty object url encoded data
Reported by: | steffanhalv | Owned by: | kadamwhite |
---|---|---|---|
Milestone: | 5.4 | Priority: | normal |
Severity: | normal | Version: | 4.7 |
Component: | REST API | Keywords: | has-patch has-unit-tests commit |
Focuses: | rest-api | Cc: |
Description
Sending empty arrays to endpoints through Rest API is not handled correctly by WP core.
Ex.
Querystring:
mysite.com/wp-json/...?my_empty_array[]=&... <- This works not
JSON:
data: { my_empty_array: [] } <- This works
When posting using json, empty array is handled correctly. When using querystring, it fails.
Please see https://github.com/woocommerce/woocommerce/issues/18249
Could also have a possible relationship with ticket:
https://core.trac.wordpress.org/ticket/42752 (https://github.com/woocommerce/woocommerce/issues/17954)
Attachments (1)
Change History (16)
This ticket was mentioned in Slack in #core-restapi by schlessera. View the logs.
7 years ago
#3
@
7 years ago
One option would be to run array_filter() on any array items in the query string, but doing so also means moving away from standard PHP URL parsing quirks.
Going with "standard PHP URL parsing quirks" also seems wrong to me, though, as the URL should be parsed in a reliable way, no matter the language the server is written in. The code should implement whatever is the expected behavior from the URL alone, even if it differs from standard PHP behavior.
#4
@
7 years ago
Yes, I agree. Current handling of empty arrays in querystrings force apps to post using json, which is very stricted by same origin policy. It can be handled by using method override though, but it would be nice to accept this in querystrings to, as querystrings is actually supposed to be supported by the api.
#5
@
6 years ago
- Focuses coding-standards added
- Severity changed from normal to blocker
This makes it impossible to delete arrays completely using query string, so it becomes a blocker for my use case.
Any progress on this?
#6
@
5 years ago
- Keywords reporter-feedback added
- Severity changed from blocker to normal
@steffanhalv could you give an example of the array you are trying to clear in the query string?
#7
@
5 years ago
@TimothyBlynJacobs I already did here https://github.com/woocommerce/woocommerce/issues/18249.
But here is some other examples:
If you have a post with the id <id> it is not possible to delete the last category or tag or role or any other 'last item of an array' using the rest api with queryparams.
This works (replacing or adding to an array ):
POST /wp/v2/posts/<id>?categories[0]=...&categories[1]=...&... POST /wp/v2/posts/<id>?tags[0]=...&... POST /wp/v2/users/<id>?roles[0]=...&...
Response = success
None of these works (trying to remove the last item of an array):
POST /wp/v2/posts/<id>?categories[]=&... POST /wp/v2/posts/<id>?tags=null&... POST /wp/v2/users/<id>?roles[]=null&... POST /wp/v2/users/<id>?roles[]=0&... POST /wp/v2/users/<id>?roles=0&... POST /wp/v2/users/<id>?roles=&... POST /wc/v2/products?attributes[0]=...&...
Response =
code:"rest_invalid_param" data:{status: 400, params: {attributes: "attributes[0] is not of type object."}} message:"Invalid parameter: attributes"
A post request to any of this endpoints has no possible ways to clear the arrays using queryparams.
Yes, JSON works but JSON is not accepted in POST requests for many servers as it has CORS restrictions which is not the case for queryparams. So the conclusion is that empty arrays is not handled correctly by the WP core.
Also please notice that I have not URL Encoded the strings in this example, but it is when Im doing the actual request.
#8
@
5 years ago
- Focuses coding-standards removed
- Keywords has-patch has-unit-tests 2nd-opinion added; reporter-feedback removed
- Milestone changed from Awaiting Review to 5.4
- Summary changed from Rest API doesnt handle empty arrays in querystring correctly to REST API: Cannot pass empty object url encoded data
Thanks for providing that context @steffanhalv! A couple of things to untangle here.
The REST API does send CORS headers. As far as I know, the intention is that any WordPress with the REST API enabled would be able to accept JSON by default: https://github.com/WordPress/wordpress-develop/blob/bf3295d10f066ef6bf670bb272aae9db400e8be5/src/wp-includes/rest-api/class-wp-rest-server.php#L239 Is there a specific deficiency you noticed in the CORS setup that WordPress Core could address?
If you were not aware, the REST API accepts form url encoded data as post data.
For the actual issue, I'm not able to replicate if the schema is setup properly. Given the following REST API route:
<?php add_action( 'rest_api_init', function () { register_rest_route( 'my-ns', 'my-route', [ 'methods' => WP_REST_Server::EDITABLE, 'args' => [ 'array' => [ 'type' => 'array', 'items' => array( 'type' => 'string', ), 'validate_callback' => 'rest_validate_request_arg', 'sanitize_callback' => 'rest_sanitize_request_arg', ], ], 'callback' => function ( WP_REST_Request $request ) { return new WP_REST_Response( $request->get_params() ); }, ] ); } );
And the given request:
curl --request POST \ --url http://trunk.test/wp-json/my-ns/my-route \ --header 'accept: application/json' \ --header 'content-type: application/x-www-form-urlencoded' \ --data array=
I receive the following response:
{ "array": [] }
If there is not items schema set, then rest_sanitize_value_from_schema()
will bail by just casting the value to an array and you'll get an array of a single string.
Taking a look at your error message, though, it looks like you are trying to set an empty object not an array. Doing a bit of research, it doesn't look like there is an RFC about how this should work, if anyone knows of one, please mention it. Languages also aren't consistent about it. For example, in nodejs.
> const qs = require( 'querystring' ); > qs.stringify( { obj: {} } ); 'obj=' > qs.parse( 'obj=' ); { obj: '' }
In the REST API currently, obj=
fails because it is not an object. And given that you can create an empty array with arr=
. I think we can allow obj=
as an empty object. There shouldn't be any BC concerns because that value is currently blocked by rest_validate_value_from_schema
, so we wouldn't be transforming a previously valid value into a different value. rest_sanitize_value_from_schema
will already convert anything invalid into an empty array so we should be good on that side as well.
I've uploaded a patch that does that for review. I'm provisionally marking this for 5.4, but again this needs review before being committed.
This ticket was mentioned in Slack in #core-restapi by timothybjacobs. View the logs.
5 years ago
This ticket was mentioned in Slack in #core-restapi by kadamwhite. View the logs.
5 years ago
#13
@
5 years ago
- Keywords commit added; 2nd-opinion early removed
- Milestone changed from 5.5 to 5.4
- Owner set to kadamwhite
- Status changed from new to assigned
This does look good, and it's my oversight that we didn't land it earlier in the cycle. I'm going to pull this back in for 5.4 as I don't see any further changes we'd need to make.
This appears to be the correct behaviour, although an unexpected one at that.
In PHP applications,
var[]=
is always treated as being set to an empty string, that means that this is interpreted asarray( '' )
.When using JSON, you can pass more specific types than what you can in a PHP Query string, which results in this being a literal
array()
All that being said, this feels a little awkward, and I'd prefer to see it standardised on the JSON behaviour, but I'm not sure it's really possible though.
One option would be to run
array_filter()
on any array items in the query string, but doing so also means moving away from standard PHP URL parsing quirks.