Ajax always processes data for requests without entity body #3438

Closed
dmethvin opened this Issue Dec 8, 2016 · 5 comments

Comments

Projects
None yet
5 participants
@dmethvin
Member

dmethvin commented Dec 8, 2016

I'm creating this ticket based on the discussion in the gh-3409 PR from @Litor so we don't forget to review it.

With ajax requests using HTTP methods that do not have a request body, such as GET, any provided data is always processed and removed, even if the caller specified processData: false. This seems contrary to the documentation which says:

The data option can contain either a query string of the form 'key1=value1&key2=value2', or an object of the form {key1: 'value1', key2: 'value2'}. If the latter form is used, the data is converted into a query string using jQuery.param() before it is sent. This processing can be circumvented by setting processData to false.

This behavior was also present before 3.x so it's not any kind of recent regression, but it does seem like a bug. I suppose you could argue that "this processing can be circumvented" refers only to serializing objects but not to appending the data to the URL, but that interpretation doesn't make sense. Why would someone want [object Object] appended to their URL? 😸

If we didn't process data at all, it would be possible for a beforeSend handler to take an object and stringify it to the URL as desired , something like this currently non-working example:

http://jsbin.com/fucigaqure/edit?html,console

@jaubourg

This comment has been minimized.

Show comment
Hide comment
@jaubourg

jaubourg Sep 17, 2017

Member

The real question is : what is the purpose of setting the data option with processData false for a GET request if the data is not to be used in the final URL ?

The data option can be set to a string that is already serialized or an object which class provides a toString method that serializes the object properly.

Conflating the processData: false with "don't use the data" seems very wrong to me.

Member

jaubourg commented Sep 17, 2017

The real question is : what is the purpose of setting the data option with processData false for a GET request if the data is not to be used in the final URL ?

The data option can be set to a string that is already serialized or an object which class provides a toString method that serializes the object properly.

Conflating the processData: false with "don't use the data" seems very wrong to me.

@gibson042

This comment has been minimized.

Show comment
Hide comment
@gibson042

gibson042 Sep 18, 2017

Member

processData: false is documented to "prevent this automatic processing [converting data to a query string and, for GET requests, appending it to the URL]". There's an argument to be made that processData should affect the jQuery.param serialization but not the URL manipulation, but that strikes me as confusing (because then processData: false doesn't actually prevent manipulation of data—or url, for that matter) and useless (as Dave points out, "why would someone want [object Object] appended to their URL?").

I have a hard time imagining any plausible purpose for jQuery.ajax(url, { type: "GET", data: nonString, processData: false, … }), but ignoring data (while exposing it to beforeSend) seems most in accord with user intentions.

No matter what, though, it's clear that http://api.jquery.com/jQuery.ajax/ needs an update.

Member

gibson042 commented Sep 18, 2017

processData: false is documented to "prevent this automatic processing [converting data to a query string and, for GET requests, appending it to the URL]". There's an argument to be made that processData should affect the jQuery.param serialization but not the URL manipulation, but that strikes me as confusing (because then processData: false doesn't actually prevent manipulation of data—or url, for that matter) and useless (as Dave points out, "why would someone want [object Object] appended to their URL?").

I have a hard time imagining any plausible purpose for jQuery.ajax(url, { type: "GET", data: nonString, processData: false, … }), but ignoring data (while exposing it to beforeSend) seems most in accord with user intentions.

No matter what, though, it's clear that http://api.jquery.com/jQuery.ajax/ needs an update.

@gibson042 gibson042 referenced this issue in jquery/api.jquery.com Sep 18, 2017

Open

Improve jQuery.ajax documentation #1061

@jaubourg

This comment has been minimized.

Show comment
Hide comment
@jaubourg

jaubourg Sep 18, 2017

Member

I hear you but I simply can't agree with a change that will make the following code not work anymore :

$.ajax( {
    url: "http://my.domain/path/to/script",
    data: "param1=value1&param2=value2",
    processData: false
} );

There are many situations where it's far simpler to serialize url parameters beforehand rather than construct a data structure for the sole purpose of accomodating jQuery's internal serialization (hence my example of an object with a custom toString() that pretty much covers the data: nonString scenario).

I think there clearly is a problem with the documentation if it implies that processData: false is supposed to prevent the addition of data to the url. The data is "not processed" (i.e. not transformed) but it's still used. If you don't want the data added to the url then don't put it in the options in the first place : no data, no problem and no need for an additional option to ignore it.

BTW, processData predates the 1.5 ajax rewrite and always behaved this way.

As an aside, where and when beforeSend is called makes using it an anti-pattern in pretty much any situation, hence the introduction of prefilters where it's safe to mess with the options. If someone wants to handle data manually like you suggest then they just need to create an option of their own together with a prefilter that checks for it and modifies the url accordingly (this won't break the ajax caching mechanism, unlike beforeSend).

Member

jaubourg commented Sep 18, 2017

I hear you but I simply can't agree with a change that will make the following code not work anymore :

$.ajax( {
    url: "http://my.domain/path/to/script",
    data: "param1=value1&param2=value2",
    processData: false
} );

There are many situations where it's far simpler to serialize url parameters beforehand rather than construct a data structure for the sole purpose of accomodating jQuery's internal serialization (hence my example of an object with a custom toString() that pretty much covers the data: nonString scenario).

I think there clearly is a problem with the documentation if it implies that processData: false is supposed to prevent the addition of data to the url. The data is "not processed" (i.e. not transformed) but it's still used. If you don't want the data added to the url then don't put it in the options in the first place : no data, no problem and no need for an additional option to ignore it.

BTW, processData predates the 1.5 ajax rewrite and always behaved this way.

As an aside, where and when beforeSend is called makes using it an anti-pattern in pretty much any situation, hence the introduction of prefilters where it's safe to mess with the options. If someone wants to handle data manually like you suggest then they just need to create an option of their own together with a prefilter that checks for it and modifies the url accordingly (this won't break the ajax caching mechanism, unlike beforeSend).

@dmethvin

This comment has been minimized.

Show comment
Hide comment
@dmethvin

dmethvin Sep 19, 2017

Member

Hi @jaubourg! Always glad to have you here.

The data is "not processed" (i.e. not transformed) but it's still used.

We do want the data to be used. One of the use cases that precipitated the ticket was that someone wanted to serialize the data to JSON rather than form-encode it.

If someone wants to handle data manually like you suggest then they just need to create an option of their own together with a prefilter that checks for it and modifies the url accordingly

Ok, so a prefilter that might take jsonData: { ... } and convert it to a stringified data object. Or alternatively, just a data object and have the prefilter decide whether to convert it based on method, specific endpoint URL, custom convertDataToJSON: true in the options, whatever.

If the jQuery.ajax() caller is treating the URL endpoint as a "black box" and doesn't want to worry about the data format then it seems like the prefilter is a good solution because it can be implemented and any scattered callers don't need to worry. If the caller is implementing the endpoint interface through their own jQuery.ajax() call wrapper then they shouldn't use processData at all and just convert the data to a string before they make the call.

If we're not making code changes I definitely think we need to make docs changes. I'm not sure I see a lot of utility in processData because it only applies to requests with entity bodies and specific transports. It's almost as if we should say "don't use this".

Member

dmethvin commented Sep 19, 2017

Hi @jaubourg! Always glad to have you here.

The data is "not processed" (i.e. not transformed) but it's still used.

We do want the data to be used. One of the use cases that precipitated the ticket was that someone wanted to serialize the data to JSON rather than form-encode it.

If someone wants to handle data manually like you suggest then they just need to create an option of their own together with a prefilter that checks for it and modifies the url accordingly

Ok, so a prefilter that might take jsonData: { ... } and convert it to a stringified data object. Or alternatively, just a data object and have the prefilter decide whether to convert it based on method, specific endpoint URL, custom convertDataToJSON: true in the options, whatever.

If the jQuery.ajax() caller is treating the URL endpoint as a "black box" and doesn't want to worry about the data format then it seems like the prefilter is a good solution because it can be implemented and any scattered callers don't need to worry. If the caller is implementing the endpoint interface through their own jQuery.ajax() call wrapper then they shouldn't use processData at all and just convert the data to a string before they make the call.

If we're not making code changes I definitely think we need to make docs changes. I'm not sure I see a lot of utility in processData because it only applies to requests with entity bodies and specific transports. It's almost as if we should say "don't use this".

@jaubourg

This comment has been minimized.

Show comment
Hide comment
@jaubourg

jaubourg Sep 19, 2017

Member

Hi @dmethvin! Always glad to argue on tickets ;)

Please note data has already been potentially processed before prefilters are called so a custom option is the only available route here.

Your "don't use this" recommandation is spot on. The only time you need it is when you provide data as a properly serialized query or as an object with a custom toString that handles the serialization. Indeed those cases are very rare. Beside using an option to change the way another option is handled is quite the anti-pattern (a rawData option would have been better but, ahem, "backward compatibility" and it doesn't provide automagical serialization from within the ajax architecture itself).

The following would definitely make processData obsolete:

$.ajaxPrefilter( options => {
    options.data = options.data || options.rawData;
} );
Member

jaubourg commented Sep 19, 2017

Hi @dmethvin! Always glad to argue on tickets ;)

Please note data has already been potentially processed before prefilters are called so a custom option is the only available route here.

Your "don't use this" recommandation is spot on. The only time you need it is when you provide data as a properly serialized query or as an object with a custom toString that handles the serialization. Indeed those cases are very rare. Beside using an option to change the way another option is handled is quite the anti-pattern (a rawData option would have been better but, ahem, "backward compatibility" and it doesn't provide automagical serialization from within the ajax architecture itself).

The following would definitely make processData obsolete:

$.ajaxPrefilter( options => {
    options.data = options.data || options.rawData;
} );

@dmethvin dmethvin closed this in d723789 Jan 16, 2018

restonexyz added a commit to restonexyz/jquery that referenced this issue Feb 3, 2018

up (#1)
* Dimensions: ignore transforms when retrieving width/height

Close gh-3561
Fixes gh-3193

* CSS: remove dead code in getWidthOrHeight

- getCSS already falls back to inline styles

Ref gh-3561

* Release: update release dependencies

* Release: update AUTHORS.txt

* Release: update version to 3.2.0-pre

* Release: md5sum -> md5 -r for MAC

* Build: Updating the master version to 3.2.1-pre.

* Release: edit dist README version on release

Fixes gh-3574

* Build: update PR template

- Comment out things we don't need to see in the PR description
- Change CLA link

* Tests: move readywait to an iframe test

Close gh-3576
Fixes gh-3573

* Dimensions: fall back to offsetWidth/Height for inline elems

Close gh-3577
Fixes gh-3571

* Revert "Event: Trigger checkbox and radio click events identically"

This reverts commit b442aba.

* Revert "Event: Add radio click triggering tests"

This reverts commit 5f35b5b.

* Tests: add test for passing trigger data to radio click handler

Close gh-3581
Fixes gh-3579

* Build: Updating the master version to 3.2.2-pre.

* CSS: retrieve inline style before computed

- Fixes an issue with getting computed style on detached elements

* Revert "Build: Updating the master version to 3.2.2-pre."

This reverts commit 066bd86.

* Build: Updating the master version to 3.2.2-pre.

* Tests: Fix incorrect assert name for ensure_iterability_es6

Closes gh-3584
Ref bb026fc.

* Docs: Update links to HTML spec for stripAndCollapse (#3594)

* Offset: Use correct offset parents; include all border/scroll values

Thanks @anseki

Fixes gh-3080
Fixes gh-3107
Closes gh-3096
Closes gh-3487

* Core: Update isFunction to handle unusual-@@toStringTag input

Ref gh-3597
Fixes gh-3600
Fixes gh-3596
Closes gh-3617

* Tests: Improve offset test setup and labels

Hopefully this fixes iOS testing: http://swarm.jquery.org/job/5226

Ref 1d2df77
Closes gh-3641

* Tests: Be even more async for iOS

Ref 1d2df77
Closes gh-3643

* Tests: Attach test iframes to the body for visibility-dependent code

Ref 1d2df77
Closes gh-3645

* Tests: Allow a mock QUnit.test for perfect testIframe fidelity

Ref 1d2df77
Closes gh-3647

* Tests: Prepend test iframes for even *more* consistency

Ref 1d2df77

* Tests: Reset iframe window scroll after updating html/document position

Ref 1d2df77
Closes gh-3649

* Tests: Add debugging to investigate iOS failures

Ref 1d2df77

* Tests: Keep iframes visible in TestSwarm

Ref 1d2df77

* Tests: Adjust by actual scroll position, rather than expected

Ref 1d2df77

* Tests: Clean up offset debugging

Ref 1d2df77
Ref c0edd8d

* Tests: Correct expected assertion count

Ref e94b5b0

* Tests: Revert some testIframe changes to fix dimensions tests

Ref c0edd8d

* Revert "Tests: Revert some testIframe changes to fix dimensions tests"

This reverts commit c4368a9.

* Tests: Revert some testIframe changes to fix dimensions tests

Ref c0edd8d

* CSS: Drop the float mapping from cssProps

Firefox 35 and newer support style.float directly.

Closes #3569

* Docs:Tests: Update IE/Edge-related support comments & tests

Closes gh-3661

* Build: Test on Node.js 8, stop testing on Node.js 7

* Tests: minor typos

Close gh-3671

* Dimensions: Include scroll gutter in "padding" box

Fixes gh-3589
Closes gh-3656

* Deferred: fix memory leak of promise callbacks

Fixes gh-3606
Closes gh-3657

* Build: update node dependencies; commit package-lock.json

- Also ignore yarn.lock
Close gh-3669

* Build: Update sinon, husky, and qunitjs

* Build: fix uglify options for uglify update

- Uses new typeofs option for compression
- See mishoo/UglifyJS2#2198

Close gh-3710

* Event: `stopPropagation()` on native event-handler

Fixes gh-3693
Close gh-3694

* Core: Deprecate jQuery.isWindow

Fixes gh-3629
Close gh-3702

* Test: ensure position/offset return mutable objects

Fixes gh-3612
Closes gh-3695

* Revert "Offset: Resolve strict mode ClientRect "no setter" exception"

This reverts commit 3befe59.

* Offset: fix error from bad merge in #3695

* Dimensions: Detect and account for content-box dimension mishandling

Fixes gh-3699
Closes gh-3700

* Support: Properly check for IE9 absolute scrollbox mishandling

Ref gh-3589
Fixes gh-3699
Fixes gh-3730
Closes gh-3729

* Tests: Try extra hard to control focus

Ref gh-3732

* Tests: Abort focus tests when the environment doesn't cooperate

Ref gh-3732

* Tests: Reduce the abort timeout for simple focus testing

Ref gh-3732

* Tests: Simulate events when CI hinders use of native ones

Ref gh-3732

* Tests: Account for TestSwarm focus issues

Closes gh-3732

* Ajax: add an ontimeout handler to all requests

Fixes gh-3586
Close gh-3590

* Dimensions: Improve offsetWidth/offsetHeight fallback

Fixes gh-3698
Fixes gh-3602
Closes gh-3738

* Tests: Replace non-ASCII whitespace in source code

* Dimensions: Don't trust non-pixel computed width/height

Fixes gh-3611
Closes gh-3741

* Build: Fix comment typo

Closes gh-3747

* Build: Update my name in .mailmap

I got married! 🎉

* Build: Update my name in ATHORS.txt

I forgot .mailmap isn't everything.

* Tests: Update path calculation

Fixes gh-3756
Closes gh-3757

* CSS: Avoid unit-conversion interference from CSS upper bounds

Fixes gh-2144
Closes gh-3745

* Tests: Update lineHeight adjustments to give Android more slop

* CSS: Detect more WebKit styles erroneously reported as percentages

Ref 692f9d4
Fixes gh-3777
Closes gh-3778

* Build: Update to Babel 7, use for-of plugin instead of preset-es2015

Closes gh-3786

* Build: Drop cross-spawn, use child_process.spawn shell option

* Build: increase timeout in Promises/A+ tests 10 times

The promises-aplus-tests sets up a default 200 ms Mocha timeout. This makes
our tests randomly fail on Jenkins. 2 seconds will be safer.

Closes gh-3791

* Build: Remove package-lock.json, add it to .gitignore

npm 5, even the version included in the latest Node.js 8.5.0 re-generates
`package-lock.json` on each install. And when it does on a system that doesn't
support all the optional dependencies that are supported on the OS where the
lockfile was generated, it removes those optional deps from the lockfile.

The effect is that everyone firing `npm install` on our repo on any OS other
than macOS will immediately get a dirty state of the repo as the `fsevents`
dependency subtree gets removed from `package-lock.json`. That's a really bad
experience.

This commit removes package-lock.json from the repository and adds it to
.gitignore. We'll start committing the file again once the issue is resolved
on npm's part.

Fixes gh-3792

* Tests: Make Node tests work for paths with spaces in them

Without this patch Jenkins tests fail as jQuery job names there contain spaces,
e.g. "jQuery Core".

Closes gh-3821

* Tests: Add Safari 11 support test results

* Build: Test on Node.js 9

Closes gh-3840

* Tests: Add iOS 11 support test results

* Manipulation: Reduce size by eliminating single-use variable

Closes gh-3851

* CSS: Correctly set support properties with non-default zoom

Fixes gh-3808
Closes gh-3872

* Docs: Create CODE_OF_CONDUCT.md

Close gh-3865

* Tests: Add support for running unit tests via grunt with karma

- Update QUnit to 1.23.1
- Remove unused dl#dl from test/index.html
- Remove unused map#imgmap from test/index.html
- Ensure all urls to data use baseURI
- Add the 'grunt karma:main' task
  - customContextFile & customDebugFile
- Add 'npm run jenkins' script

Close gh-3744
Fixes gh-1999

* Build: Only run browser tests in one Node version on Travis

Ref gh-3744
Closes gh-3894

* Core: make camelCase function available only for internal usage

Close gh-3604
Fixes gh-3384

* Core: adjust data tests to ensure proper camelCasing

- Add back camelCase to the public object (deprecate not remove)
Ref #3384

* Core: deprecate jQuery.now

Fixes gh-2959
Close gh-3884

* Core: deprecate jQuery.proxy (not slated for removal)

Fixes gh-2958
Close gh-3885

* Manipulation: use `.children` to select tbody elements

- selectors beginning with a child combinator are not valid natively.
  This fixes the tests when using selector-native.js

* Attributes: allow array param in add/remove/toggleClass

+30 bytes instead of +182

Thanks to @faisaliyk for the first pass on this feature.

Fixes gh-3532
Close gh-3917

* Ajax: add unit test for getScript(Object)

Fixes gh-3736
Close gh-3918

* Tests: only run ontimeout test if ontimeout exists

Fixes gh-3742
Close gh-3919

* Build: Fix UglifyJS output in Android 4.0; update uglify

- Thanks to @mgol for first pass

Fixes gh-3743
Close gh-3920

* Tests: fix function reference for unbinding

Ref gh-2958

* Build: Remove CRLF line endings to fix builds on Windows

Close gh-3929

* Core: deprecate jQuery.isFunction

Fixes gh-3609

* Event: Move event aliases to deprecated

Fixes gh-3214

* Ajax: Don't process non-string data property on no-entity-body requests

Fixes gh-3438
Closes gh-3781

* Core: deprecate jQuery.isNumeric

Fixes gh-2960
Closes gh-3888

* Tests: fix weird failure in Edge 16 CSS

Fixes gh-3866
Close gh-3932

* Tests: fix weird flaky attributes test in Edge 16

Fixes gh-3867
Close gh-3931

* Core: deprecate jQuery.type

Fixes gh-3605
Close gh-3895

* Tests: fix number of expected assertions in basic core

* Tests: temporarily require sudo access for karma:main on travis

- This should fix the broken travis build on Node 8
- See travis-ci/travis-ci#8836

* Tests: correctly set sudo in travis config, not karma config

* Manipulation: Add support for scripts with module type

Fixes gh-3871
Close gh-3869

* Tests: fix tests in AMD mode

* Tests: ensure that module assertions run on supported browsers

- Also fixes tests for karma, where the URL for the module is different

Ref gh-3871

* Filter: Use direct filter in winnow

for both simple and complex selectors

Fixes gh-3272
Closes gh-3910

* Build: Add "-debug" suffix to name of karma debug tasks

Ref gh-3922
Close gh-3936

* Tests: skip test with invalid selector for selector-native tests

* Release: add new authors to AUTHORS.txt

* Release: update version to 3.3.0-pre

* Build: Updating the master version to 3.3.1-pre.

* Build: Updating the master version to 3.3.2-pre.

@jquery jquery locked as resolved and limited conversation to collaborators Jul 15, 2018

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