Content Negotiation

In HTTP, Content Negotiation is a mechanism used to provide various versions of resources for the same URL. BEAR.Sunday supports server-side content negotiation of media type ‘Accept’ and ‘Accept-Language’ of language. It can be specified on an application basis or resource basis.

Install

Install BEAR.Accept with composer

composer require bear/accept ^0.1

Next, save the context corresponding to the Accept * request header in /var/locale/available.php

<?php
return [
    'Accept' => [
        'text/hal+json' => 'hal-app',
        'application/json' => 'app',
        'cli' => 'cli-hal-app'
    ],
    'Accept-Language' => [ // lower case for key
        'ja-jp' => 'ja',
        'ja' => 'ja',
        'en-us' => 'en',
        'en' => 'en'
    ]
];

The Accept key array specifies an array whose context is a value with the media type as a key. cli is not used in web access in the context of console access.

The Accept-Language key array specifies an array with the context key as the key for the language.

Enable by Application

Change public/index.php to enable content negotiation throughout the application.

<?php
use BEAR\Accept\Accept;

require dirname(__DIR__) . '/vendor/autoload.php';

$accept = new Accept(require dirname(__DIR__) . '/var/locale/available.php');
list($context, $vary) = $accept($_SERVER);

require dirname(__DIR__) . '/bootstrap/bootstrap.php';

For example, in the above setting, the access context of the following Accept* header will be prod-hal-ja-app.

Accept: application/hal+json
Accept-Language: ja-JP

At this time JaModule requires binding for Japanese text. For details, refer to the demo application MyVendor.Locale.

Enable by Resource

To do content negotiation on a resource basis, install the AcceptModule module and use the @Produces annotation.

Module Install

protected function configure()
{
    // ...
    $available = $appDir . '/var/locale/available.php';
    $this->install(new AcceptModule(available));
}

@Produces annotation

use BEAR\Accept\Annotation\Produces;

/**
 * @Produces({"application/hal+json", "text/csv"})
 */
public function onGet()

Annotate available media type from left by priority. The representation (JSON or HTML) is changed by the contextual renderer. You do not need to add Vary header manually unlike application level content-negotiation.

Access using curl

Specify the Accept* header with the -H option.

curl -H 'Accept-Language: en' http://127.0.0.1:8080/
curl -i -H 'Accept-Language: en' -H 'Accept: application/hal+json' http://127.0.0.1:8080/
HTTP/1.1 200 OK
Host: 127.0.0.1:8080
Date: Fri, 11 Aug 2017 08:32:33 +0200
Connection: close
X-Powered-By: PHP/7.1.4
Vary: Accept, Accept-Language
content-type: application/hal+json

{
    "greeting": "Hello BEAR.Sunday",
    "_links": {
        "self": {
            "href": "/index"
        }
    }
}