Render an array based on accept header
I’m currently working on an API using Slim 3 and needed a generic way to render arrays to XML, JSON or HTML based on the Request’s Accept header.
This is just good practice. The Accept header is used by a client to specify the media types that it accepts. Therefore if our client would like XML, I’d like to give it XML. Similarly for JSON. I also like to support an HTML rendering on my API if I can as it makes exploration much easier.
This seemed like an ideal thing for a component and so I wrote rka-content-type-renderer! (One day I’ll work out how to name things, but for now, if it starts with rka, then it’s probably a reusable component!)
It’s PSR-7 compliant because if you’re going to write something that deals with HTTP nowadays, PSR-7 is the only sensible interface to target. Therefore, this component renders to a PSR-7’s Response object’s body.
Usage is really simple:
$data = [ 'items' => [ [ 'name' => 'Alex', 'is_admin' => true, ], [ 'name' => 'Robin', 'is_admin' => false, 'link' => 'http://example.com', ], ], ]; $renderer = new \RKA\ContentTypeRenderer\Renderer(); $response = $renderer->render($request, $response, $data); return $response->withStatus(200);
The $request and $response objects must implement PSR-7’s RequestInterface and ResponseInterface respectively. This makes it very easy to use within say Radar, Expressive, Slim or any other system that can bridge to PSR-7.
In this case, the output for JSON is:
{ "items": [ { "name": "Alex", "is_admin": true }, { "name": "Robin", "is_admin": false, "link": "http:\/\/example.com" } ] }
& for XML:
<?xml version="1.0"?> <root> <items> <name>Alex</name> <is_admin>1</is_admin> </items> <items> <name>Robin</name> <is_admin>0</is_admin> <link>http://example.com</link> </items> </root>
Finally, it will also render the array as an HTML unsigned list if the content-type is text/html which makes examining an API using a web browser very easy and worth doing:
- items:
- 0:
- name: Alex
- is_admin: true
- 1:
- name: Robin
- is_admin: false
- link: http://example.com
- 0:
Obviously, in the HTML, we link any links so that the user can explore the API data, simply by clicking.
Hal support
While doing this, I also added support for rendering Nocarrier/Hal objects too as the HAL specification supports both JSON and XML, so it seems logical to send the correct format back to the client based on what they’ve asked for.
All in all, I’ve found this to be a useful component and if you have a similar need, then maybe this will help.
Hey Rob,
Thank you. You did a great job.
But I think you find something missing in the PSR-7? We need some way to have a Stream which can be used by any psr-7 implementations. What do you think about it?
Hari,
Could you expand what you mean? rka-content-type-renderer will use the stream attached to the supplied response unless it can't, in which case it uses a simple one.
Have you considered the use of a fully-fledged content negotiation component?
The Render::determineMediaType() lacks the sophistication needed to robustly parse Accept fields.
I maintain both a stand-alone (https://github.com/ptlis/conneg) and PSR-7 (https://github.com/ptlis/psr7-conneg) library that solves this problem.
Brian,
I already have willdurand-negotiation ready to go in a branch. Just need to push and release.
Ah, I didn't spot that – no worries
Though I would drop by and note there is already a middleware for content negotiation that works with Slim 3, you can get it here https://github.com/rszrama/negotiation-middleware.