Merry Christmas! This year Santa Claus brought me an esoteric bug in the Joomla API application.
Tagging @wilsonge as he's the only contributor of the code in play, therefore the only one who will understand what I am talking about.
If you want to tell the Joomla API Application to provide a public API which handles requests with a different Accept header than the Joomla default of application/vnd.api+json
you need to have the following in your webservices
plugin:
$defaults = [
'component' => 'com_example',
'public' => true,
'format' => [
'application/ld+json',
'application/json'
]
];
$router->addRoute(
new Route(
['GET'], 'v1/example/whatever/:id', 'whatever.displayItem', ['id' => '[\d]+'], $defaults
)
);
The format
in the parameters is used in the ApiApplication::route()
method to override the priorities of the Negotiator object:
joomla-cms/libraries/src/Application/ApiApplication.php
Lines 248 to 254 in f520c98
This works GREAT!
It works so well that once the negotiator settles for one of the provided priorities, ApiApplication correctly sets it as the negotiated content type:
joomla-cms/libraries/src/Application/ApiApplication.php
Lines 267 to 274 in f520c98
This also works GREAT!
But.
Right away, Joomla overwrites the negotiated content type with the entire array I had to pass through my plugin
joomla-cms/libraries/src/Application/ApiApplication.php
Lines 284 to 292 in f520c98
Remember, $route['vars']
has everything I passed in the $default
of my plugin. I had to pass an array keyed format
with all my valid Accept headers, right? The Joomla API application uses the a request parameter called format
of expected type string to figure out the content type and from there the Joomla Document type, right? The two things are named the same, have different expected data types (array vs string) which means that I get an immediate PHP error: Argument #1 ($type) must be of type string, array given
Right now you have to use the onAfterApiRoute
plugin event handler to run the Negotiator AGAIN and set the format
input variable AGAIN to work around the Joomla issue.
Add the following after line 284:
if ($key === 'format') {
continue;
}
Labels |
Added:
No Code Attached Yet
|
Oh, man, get well soon! It's nothing urgent. This will probably take months to complete. Once it's done you're gonna be surprised at what you can do with the Joomla API application
@nikosdion I wonder if is enough just set up in the onAfterApiRoute the follow:
$app->input->set('format', 'application/json');
without run the Negotiator again
@carlitorweb Not unless you want to break every other API application integration. This would have Joomla looking for JsonView in all API requests instead of JsonapiView.
You can of course check if it's your component and use domain-specific knowledge to assume a different content type for your own component's API application integration only, without running the negotiator. I am veering towards that direction, actually.
@nikosdion yes, sorry, I meant for a custom component, should be enough just add that.
Again, ONLY if you do that IF AND ONLY IF you have checked $input->getCmd('option') and it matches your component.
Custom component or not, the onAfterApiRoute
event is called always. Let's not tell people to do something potentially stupid which will break the JSON API for everyone else. I have seen other 3PDs do… things in their plugins over the years. Give them half a malformed sentence to grip on and they'll cock up something major. I would classify this as being Joomla's fault for neither educating 3PDs, nor policing this "extension A breaks all other extensions" problem on the JED.
Managed to get this written up whilst I was on the plane home. Now 12 hour return home from girlfriends parents accomplished. I'm off to sleep. Apologies if there's anything dumb in the PR description but I'm sick and tired and running on fumes!
Status | New | ⇒ | Closed |
Closed_Date | 0000-00-00 00:00:00 | ⇒ | 2022-12-26 14:43:13 |
Closed_By | ⇒ | wilsonge |
Well bugger :D been a bit sick last few days so might take me another day or two to get the PR in but I guess may as well skip all 4 input objects we set directly above for good measure. Glad to know it works otherwise tho :)