-
-
Notifications
You must be signed in to change notification settings - Fork 846
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
Update ErrorFormatGuesser to handle custom formats and improve guess… #1656
Conversation
Travis check failed because their service was down yesterday, can we please restart the tests because it should pass. Thank you. |
src/Util/ErrorFormatGuesser.php
Outdated
@@ -37,14 +37,35 @@ private function __construct() | |||
public static function guessErrorFormat(Request $request, array $errorFormats): array | |||
{ | |||
$requestFormat = $request->getRequestFormat(''); | |||
if ('' !== $requestFormat && isset($errorFormats[$requestFormat])) { | |||
|
|||
if ('' !== $requestFormat && array_key_exists($requestFormat, $errorFormats)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this change? (isset
is faster)
src/Util/ErrorFormatGuesser.php
Outdated
} | ||
} | ||
|
||
return ['key' => key($errorFormats), 'value' => reset($errorFormats)]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we already loop over formats, you can just store the first format in a var, line 48:
$requestMimeTypes = $request->getMimeTypes($request->getRequestFormat());
$defaultFormat = [];
foreach ($errorFormats as $format => $errorMimeTypes) {
if ([] === $defaultFormat) {
$defaultFormat = ['key' => $format, 'value' => $errorMimeTypes];
}
if (array_intersect($requestMimeTypes, $errorMimeTypes) {
return ['key' => $format, 'value' => $errorMimeTypes];
}
}
return $defaultFormat;
It's faster and safer in case no default format is defined.
src/Util/ErrorFormatGuesser.php
Outdated
/** | ||
* Get MIME type from the request. | ||
* | ||
* @param \Symfony\Component\HttpFoundation\Request $request |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@param
and @return
can be removed because of the typehints.
src/Util/ErrorFormatGuesser.php
Outdated
* | ||
* @return string|null | ||
*/ | ||
private static function getMimeType(Request $request): ?string |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this method and just call $request->getMimeTypes($request->getRequestFormat())
because the content negotiation is already properly handled here: https://github.com/api-platform/core/blob/master/src/EventListener/AddFormatListener.php#L67
src/Util/ErrorFormatGuesser.php
Outdated
$mimeType = self::getMimeType($request); | ||
|
||
foreach ($errorFormats as $format => $mimeTypes) { | ||
if (in_array($mimeType, $mimeTypes, true)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sometimes they are several mime types associated with one formats (it's why you should call $request->getMimeTypes()). They all must be checked. See my alternative proposal below.
Can you also target the master? It's a new feature to me. |
@dunglas I've made the changes, thank you for the pieces of code you gave me. I don't know how to target master instead of 2.1, do you want me to create a new PR to master? |
@natepage |
Rebased and RFR @api-platform/core-team. |
src/Util/ErrorFormatGuesser.php
Outdated
$defaultFormat = []; | ||
|
||
foreach ($errorFormats as $format => $errorMimeTypes) { | ||
if ([] === $defaultFormat) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what? this looks weird can we change to empty? or !$defaultFormat
? Why can't we init above with the proper array?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because we not sure the key 0 exist, it looks ok to me to do this. We indeed may use !$defaultFormat
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd be 👍 for !$defaultFormat
. Anyway I panicked because [] === []
is false in javascript 😆
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
…nstead of private function.
Thanks @natepage and @meyerbaptiste! |
Wanted to implement custom error formats based on the format requested by the client, the issue was that whatever the request format the errors were always in the first error format configured. This PR has for purpose to improve the way the
ErrorFormatGuesser
is guessing the current format from the request and the configuration.The logic is as following:
1- Check if request format has been set
2- Check if configuration for the format exist and return it if Yes
3- Otherwise guess MIME type from request headers (Accept, Content-Type)
4- Check if configuration for the MIME type exist and return it if Yes
5- Otherwise fallback to first error format configured
Example of configuration:
Return table
['key' => 'custom_error_xml', 'value' => ['application/xml', 'text/xml']]
['key' => 'custom_error_xml', 'value' => ['application/xml', 'text/xml']]
['key' => 'custom_error_json', 'value' => ['application/json']]
['key' => 'custom_error_xml', 'value' => ['application/xml', 'text/xml']]
['key' => 'custom_error_xml', 'value' => ['application/xml', 'text/xml']]
The priority order to guess the format is: Request format > Accept Header > Content-Type Header > Fallback to first error format configured.