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

[PHP] Allow selecting Content-Type #13769

Merged
merged 5 commits into from
Oct 26, 2022

Conversation

thomasphansen
Copy link
Contributor

The current HeaderSelector class is handling Content-Type incorrectly:

  1. When there are multiple Content types in the OpenAPI Definition and none are of type application/json, it is concatenating them. This is explicitly described as incorrect in IETF RFC9110, section 8.3, last paragraph:

Although Content-Type is defined as a singleton field, it is sometimes incorrectly generated multiple times, resulting in a combined field value that appears to be a list. Recipients often attempt to handle this error by using the last syntactically valid member of the list, leading to potential interoperability and security issues if different implementations have different error handling behaviors.

  1. If any of the multiple content types contains "application/json", then it will always set the Content Type as "application/json" - potentially ignoring parameters given to that content type. Many services expect to receive parameters in the Content-Type header: they can be used, for example to define the charset, restrict the amount of data to be received or define the standard to be used when reading floating-point fields. Consider the following examples:
Content-Type: application/json; IEEE754Compatible=true
Content-Type: application/json; charset=ISO-8859-4
Content-Type: application/json; odata.metadata=minimal

The first one is critical when sending data to Microsoft Business Central: without it, sending a numeric (floating point) field through POST will result in 500 error.

  1. Third-party OpenAPI definitions may offer multiple content-types for application/json, each one with different parameters. Right now, it is not possible to choose between them.

In order to solve this issues, and after discussing them with @wing328, I'm presenting this PR to add the Content-Type as an extra parameter to the operation calls.

I thought about creating constants for the Content-Types, but normalizing them in order to create the constant names (like we do for field names and enum constants, for example) would require changes to the PhpClientCodegen. To avoid that, I chose to create a constant array to hold the possible Content-Types for each operation. In this way, we can provide a list of possible values for the $contentType parameter, and also validate it.

I've also discussed changes to the Accept header. They will come in a separate PR, to keep it small.

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package 
    ./bin/generate-samples.sh
    ./bin/utils/export_docs_generators.sh
    
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    For Windows users, please run the script in Git BASH.
  • File the PR against the correct branch: master (6.1.0) (minor release - breaking changes with fallbacks), 7.0.x (breaking changes without fallbacks)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.
    @jebentier, @dkarlovi, @mandrean, @jfastnacht, @ybelenko, @renepardon

@thomasphansen
Copy link
Contributor Author

After a bit of rethinking, I decided to remove the validation of the Content-Type.

The OpenAPI Specification doesn't offer a way to describe parameters for Content-Type media types. A service may offer different options based on the content-type parameters, and they may not be described as separate content-types in the OpenAPI definition.

For example, oData-based API's offer several parameters that can be used to limit the amount of data to be sent back to the client. Examples:

Content-Type: application/json; odata.metadata=minimal; odata.streaming=true
Content-Type: application/json; odata=verbose
Content-Type: application/json; odata.metadata=none

Some of these parameters may make sense in some contexts, but not in others: for example, if you are doing a full search in a big table, you may use "odata.metada=minimal" to reduce the amount of data to be retrieved, but if the search of for a specific subset and you will perform an update on it later, then you will need the full metadata information.

By removing the validation on the field, we open a big door for Content-Type, allowing the client user to adjust its parameters on the fly - instead of relying on what is hard-coded in the OpenAPI definition.

Hope you guys agree! 😄

{
$headers = [];

$accept = $this->selectAcceptHeader($accept);
if ($accept !== null) {
$headers['Accept'] = $accept;
}
if(!$isMultipart) {
if($contentType === '') {
$contentType = 'application/json';
Copy link
Member

Choose a reason for hiding this comment

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

FYI. We can keep 'application/json' as the default for the time being if nothing is provided. For some other clients, we've removed such default as some servers do not expect "application/json" as the content-type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure about what you want me to do here - the current code is doing exactly that: keeping application/json as default if nothing is provided... The "$isMultipart" check is just a rewrite of what the previous code used to do: if multipart is set, don't send any contentType header...

Copy link
Member

Choose a reason for hiding this comment

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

Nothing to do. More of an FYI that something we need to change later.

@wing328
Copy link
Member

wing328 commented Oct 26, 2022

@wing328 wing328 merged commit 824b2aa into OpenAPITools:master Oct 26, 2022
@thomasphansen thomasphansen deleted the th_php_fix_content_type branch October 26, 2022 10:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants