Content-Type Negotiation in CakePHP 4.4
During my workshop at Cakefest 2022 I covered the new content-type negotiation features shipped in CakePHP 4.4. I wanted to share that information here so it is more easily found in the future.
What is Content-Type negotiation anyways?
Content-type negotiation is a feature that allows APIs and their clients to ‘negotiate’ content types. Most commonly, clients use either the Accept
header or a URL extension to declare what kind of content they would like. Servers can then reply with a 200
and a matching Content-Type
header. If the server can’t respond with that content-type then the server should respond with a 40x
status code.
Content-Type negotiation in 4.4
Historically, content-type negotiation was done by RequestHandlerComponent
. While this works well for simple content types, adding new content types, and enabling content-types for a subset of your controllers wasn’t as simple as it should be. Additionally, there was no way for application developers to replace the fallback view classes should negotiation fail. With the new APIs, Controllers use a declarative interface to list the view classes they want to negotiate with. Each view declares which content-type it can generate, or uses ``TYPE_MATCH_ALL`` to become a fallback view. With these methods CakePHP can perform negotiation for client requests.
- // In a controller
- {
- // Use a customized Json view or fallback to the default JsonView.
- return [CustomJsonView::class, JsonView::class];
- }
By implementing Controller::viewClasses()
your controller will opt-in to the new content-type negotiation system. When using Controller::viewClasses()
you should take care to remove RequestHandler
component from your controller.
In order for a view class to participate in content-type negotiation it needs to implement the contentType
method. This method is used by CakePHP to match views with client preferences. For example, our custom JSON view would have the following method:
- // In src/View/CustomJsonView.php
- public static function contentType(): string
- {
- return 'application/vnd.app+json';
- }
With the controller and view methods implemented, clients can now send Accept: application/vnd.app+json
and receive a response rendered by your custom json view class. If you’re using a custom content-type like this and want to use your view with extension based routing, you’ll need to enable the extension in your route configuration:
- // in config/routes.php
- $routes->scope('/', function(RouteBuilder $builder) {
- $builder->addExtensions(['appjson']);
- });
Next, we connect the extension to the content-type. In your controller’s beforeFilter()
or beforeRender()
methods add the following:
- // In a controller beforeFilter()
- // Bind the json extension type to our custom content type.
- $this->response->setTypeMap('appjson', ['application/vnd.app+json']);
With our new content-type linked to an extension, clients can would be able to use either the Accept
header or .appjson
in URLs and have the response generated by your custom view class.
These APIs are available as of CakePHP 4.4.0, so you can start using them today. As always, any feedback on these features or any part of CakePHP can be done on GitHub
There are no comments, be the first!