Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix handling of empty request content by allowing to disable listeners per operation #2757

Merged

Conversation

teohhanhui
Copy link
Contributor

@teohhanhui teohhanhui commented Apr 23, 2019

Q A
Bug fix? yes?
New feature? kinda?
BC breaks? no
Deprecations? no
Tests pass? yes
Fixed tickets #1282, #2731
License MIT
Doc PR TODO

The toggleable listeners are:

  • ReadListener ("read")
  • DeserializeListener ("deserialize")
  • ValidateListener ("validate")
  • WriteListener ("write")
  • SerializeListener ("serialize")

Basically, we fix the handling of empty request content by removing explicit handling of empty request content (which was wrong, resulting in weird bugs such as #2731), but allowing the user to be in control instead.

TODO:

  • Fix tests
  • Add tests
  • GraphQL support

@soyuka
Copy link
Member

soyuka commented Apr 24, 2019

Thanks to this you'd have the ability to remove some listeners (@ApiResource(attributes={"read"=false})) ?
What are the use cases? Why isn't it possible to send an empty body anymore?

I also don't get the link with #1282

@teohhanhui
Copy link
Contributor Author

teohhanhui commented Apr 24, 2019

Thanks to this you'd have the ability to remove some listeners (@ApiResource(attributes={"read"=false})) ?

Yes.

Why isn't it possible to send an empty body anymore?

I don't get what you mean. It's possible to send an empty body if you set "deserialize"=false (and possibly "validate"=false, depending on the use case).

@teohhanhui
Copy link
Contributor Author

teohhanhui commented Apr 24, 2019

Well, we fix #1282 by doing the right thing out of the box, and allowing the user to disable specific listeners so that they're able to send empty body, as necessary.

Or in other words, no weird attempts by us to try to read the user's mind. 🙈

@teohhanhui
Copy link
Contributor Author

But I'll add tests to show that.

@soyuka
Copy link
Member

soyuka commented Apr 24, 2019

I don't get what you mean. It's possible to send an empty body if you set "deserialize"=false (and possibly "validate"=false, depending on the use case).

You removed a functional test, in a way a promise that this use case works :p. I'm not fond of these new attributes though, they're scarry lol, the wdyt @dunglas ?

Or in other words, no weird attempts by us to try to read the user's mind. see_no_evil

Couldn't we fix this by implementing the following table instead?

content-type body content result notes
not set empty process the request
not set with data 406 throw not acceptable: content-type is mandatory
set empty may throw 400 if deserializer fails an empty string is valid text but invalid json
set with data process the request

@teohhanhui
Copy link
Contributor Author

teohhanhui commented Apr 24, 2019

You removed a functional test, in a way a promise that this use case works :p.

Because it's wrong. I'll add new ones.

Couldn't we fix this by implementing the following table instead?

No. The interpretation depends on the content-type. We don't have enough information to make that decision. It's better to leave it up to the user. But we do the right thing by default, by leaving that up to the Serializer.

Copy link
Member

@dunglas dunglas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea, but these new attributes must be honored in GraphQL resolvers too.

features/main/crud.feature Outdated Show resolved Hide resolved
src/Bridge/Symfony/Bundle/Resources/config/api.xml Outdated Show resolved Hide resolved
@dunglas
Copy link
Member

dunglas commented Apr 24, 2019

Also, couldn't this replace the _api_receive, _api_respond and _api_persist request attributes?

@teohhanhui
Copy link
Contributor Author

teohhanhui commented Apr 24, 2019

Also, couldn't this replace the _api_receive, _api_respond and _api_persist request attributes?

Yes, but we'd need to add the same for:

  • AddFormatListener ("negotiate")
  • RespondListener ("send")

We could rename the listeners too (and deprecate the old names).

So the whole chain would be:

negotiate -> read -> deserialize -> validate -> write -> serialize -> send

Any of the steps can be disabled individually.

WDYT @dunglas?

@teohhanhui
Copy link
Contributor Author

The semantics would be of interest to the built-in events implementation. /cc @alanpoulain

@teohhanhui
Copy link
Contributor Author

Also, couldn't this replace the _api_receive, _api_respond and _api_persist request attributes?

@dunglas Another difficulty is that _api_respond is also used in SwaggerUiListener and ExceptionListener. I'm not sure what we should do about that.

@teohhanhui teohhanhui force-pushed the fix/handling-empty-request-content branch from caba84f to 7fb57fa Compare April 25, 2019 18:24
@teohhanhui teohhanhui force-pushed the fix/handling-empty-request-content branch 2 times, most recently from 5db69a4 to a35bf0d Compare April 26, 2019 18:14
@teohhanhui teohhanhui requested a review from dunglas April 26, 2019 18:16
@teohhanhui
Copy link
Contributor Author

Could someone help implement the GraphQL support?

@alanpoulain? 😄

@teohhanhui
Copy link
Contributor Author

@dunglas Could GraphQL support be added in a separate PR (in master)? My primary aim here is to fix a bug. It's only as a result of that that we end up introducing a new feature. 😆

features/main/crud.feature Outdated Show resolved Hide resolved
features/main/crud.feature Outdated Show resolved Hide resolved
…s per operation

The toggleable listeners are:
- ReadListener ("read")
- DeserializeListener ("deserialize")
- ValidateListener ("validate")
- WriteListener ("write")
- SerializeListener ("serialize")
@teohhanhui teohhanhui force-pushed the fix/handling-empty-request-content branch from a35bf0d to b1590b8 Compare May 6, 2019 16:20
@teohhanhui teohhanhui merged commit d806c72 into api-platform:2.4 May 6, 2019
@teohhanhui teohhanhui deleted the fix/handling-empty-request-content branch May 6, 2019 16:39
@soyuka
Copy link
Member

soyuka commented May 7, 2019

This needs to be documented asap

@maks-rafalko
Copy link
Contributor

This needs to be documented asap

Here I'm crying again. Sorry - we are trying to be up-to-date with new stable releases in quite a big project.

Our tests failing for 2.4.3, and I spent quite a bit of time looking to the changelog, commits between 2.4.2 and 2.4.3 and searching in 3 repos something similar to this issue to find that this feature is not documented (I'm not even saying that this is a BC break in patch version).

Could you please revise the way you guys deploy new features? If it's released, please make sure it's documented. Please make sure there is a note in the changelog.

@dunglas
Copy link
Member

dunglas commented May 21, 2019

@borNfreee I agree, I missed it wasn't targeting master. Maybe should we revert this patch and merge it only in master. Also, it introduces an inconsistency with GraphQL. WYDT @teohhanhui? Can I revert this patch safely?

@maks-rafalko
Copy link
Contributor

If you guys decide to revert it, please tag a 2.4.4 if that is possible ASAP, since now everyone who wants to update a patch version will have a problem

@teohhanhui
Copy link
Contributor Author

It's a bug fix. I don't see why it should be reverted.

But we should (I should) add the documentation.

@dunglas
Copy link
Member

dunglas commented May 21, 2019

It breaks existing applications that are just upgrading to a new patch version, it’s not acceptable.

@teohhanhui
Copy link
Contributor Author

teohhanhui commented May 21, 2019

It's only for custom operations which used an empty request body. Our previous hack introduced other bugs, such as throwing an unexpected and cryptic exception when the request body is empty.

There's no way to maintain compatibility with buggy behavior. So by definition a bug fix must break what depended on the buggy behavior.

#2757 (comment)

@teohhanhui
Copy link
Contributor Author

I know this is overused, but...

On a more serious note, fixing bugs inevitably breaks apps that were depending on the buggy behavior. We could try to minimize that impact, but it should not dictate how we should / should not fix a bug.

@dunglas
Copy link
Member

dunglas commented May 21, 2019

We could at least agree to not merge bug fixes introducing BC breaks in patch releases. They should at least be in minor releases. This one is a true BC break, not just an edge case... because the old behavior was covered by a test.

It's too late to revert now, but could you add a note in the CHANGELOG explaining how to fix this BC break please?

@teohhanhui
Copy link
Contributor Author

All bug fixes are BC breaks. We have many test cases that are wrong. It's an unavoidable problem.

could you add a note in the CHANGELOG

I'm on it.

@teohhanhui
Copy link
Contributor Author

See #2815

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants