zf-content-negotiation
is a module for automating content negotiation tasks within a Zend
Framework 2 application.
The following features are provided
- Mapping
Accept
header media types to specific view model types, and automatically casting controller results to those view model types. - Defining
Accept
header media type whitelists; requests withAccept
media types that fall outside the whitelist will be immediately rejected with a406 Not Acceptable
response. - Defining
Content-Type
header media type whitelists; requests sending content bodies withContent-Type
media types that fall outside the whitelist will be immediately rejected with a415 Unsupported Media Type
response.
Please see the composer.json file.
Run the following composer
command:
$ composer require zfcampus/zf-content-negotiation
Alternately, manually add the following to your composer.json
, in the require
section:
"require": {
"zfcampus/zf-content-negotiation": "^1.2"
}
And then run composer update
to ensure the module is installed.
Finally, add the module name to your project's config/application.config.php
under the modules
key:
return [
/* ... */
'modules' => [
/* ... */
'ZF\ContentNegotiation',
],
/* ... */
];
If you use zf-component-installer, that plugin will install zf-content-negotiation as a module for you.
The top-level configuration key for user configuration of this module is zf-content-negotiation
.
The controllers
key is utilized for mapping a content negotiation strategy to a particular
controller service name (from the top-level controllers
section). The value portion
of the controller array can either be a named selector (see selectors
below), or a
selector definition.
A selector definition consists of an array with the key of the array being the name of a view model, and the value of it being an indexed array of media types that, when matched, will select that view model.
Example:
'controllers' => [
// Named selector:
'Application\Controller\HelloWorld1' => 'Json',
// Selector definition:
'Application\Controller\HelloWorld2' => [
'ZF\ContentNegotiation\JsonModel' => [
'application/json',
'application/*+json',
],
],
],
The selectors
key is utilized to create named selector definitions for reuse between many different
controllers. The key part of the selectors array will be a name used to correlate the selector
definition (which uses the format described in the controllers key).
Example:
'selectors' => [
'Json' => [
'ZF\ContentNegotiation\JsonModel' => [
'application/json',
'application/*+json',
],
],
],
A selector can contain multiple view models, each associated with different media types, allowing you to provide multiple representations. As an example, the following selector would allow a given controller to return either JSON or HTML output:
'selectors' => [
'HTML-Json' => [
'ZF\ContentNegotiation\JsonModel' => [
'application/json',
'application/*+json',
],
'ZF\ContentNegotiation\ViewModel' => [
'text/html',
],
],
],
The accept_whitelist
key is utilized to instruct the content negotiation module which media types
are acceptable for a given controller service name. When a controller service name is configured
in this key, along with an indexed array of matching media types, only media types that match
the Accept
header of a given request will be allowed to be dispatched. Unmatched media types
will receive a 406 Cannot honor Accept type specified
response.
The value of each controller service name key can either be a string or an array of strings.
Example:
'accept_whitelist' => [
'Application\\Controller\\HelloApiController' => [
'application/vnd.application-hello+json',
'application/hal+json',
'application/json',
],
],
The content_type_whitelist
key is utilized to instruct the content negotiation module which media
types are valid for the Content-Type
of a request. When a controller service name is
configured in this key, along with an indexed array of matching media types, only media types
that match the Content-Type
header of a given request will be allowed to be dispatched. Unmatched
media types will receive a 415 Invalid content-type specified
response.
The value of each controller service name key can either be a string or an array of strings.
Example:
'content_type_whitelist' => [
'Application\\Controller\\HelloWorldController' => [
'application/vnd.application-hello-world+json',
'application/json',
],
],
- Since 1.3.0
This boolean flag determines whether or not the HttpMethodOverrideListener
will be enabled by default.
- Since 1.3.0
The http_override_methods
key is utilized to provide the
HttpMethodOverrideListener
with a map of allowed override methods for a given
HTTP method, as specified via the X-HTTP-Method-Override
header. Essentially,
the values are:
'Incoming HTTP request method' => $arrayOfAllowedOverrideMethods,
As an example, if you want to allow the X-HTTP-Method-Override
header to allow
overriding HTTP GET
requests with an alternate method, you might define this
as follows:
'x_http_method_override_enabled' => true,
'http_override_methods' => [
'GET' => [
'HEAD',
'POST',
'PUT',
'DELETE',
'PATCH',
],
];
Then, to make a request, you could do the following:
GET /foo HTTP/1.1
Host: example.com
X-HTTP-Method-Override: PATCH
some=content&more=content
The above would then be interpreted as a PATCH
request. If the same request
were made via HTTP POST
, or if a GET
request were made with an override
value of OPTIONS
, the listener would raise a problem, as, in the former case,
no maps are defined for POST
, and, in the latter, OPTIONS
is not in the map
for GET
.
The following configuration is provided in config/module.config.php
to enable the module to
function:
'filters' => [
'aliases' => [
'Zend\Filter\File\RenameUpload' => 'filerenameupload',
],
'factories' => [
'filerenameupload' => Factory\RenameUploadFilterFactory::class,
],
],
'validators' => [
'aliases' => [
'Zend\Validator\File\UploadFile' => 'fileuploadfile',
],
'factories' => [
'fileuploadfile' => Factory\UploadFileValidatorFactory::class,
],
],
'service_manager' => [
'factories' => [
ContentTypeListener::class => InvokableFactory::class,
'Request' => Factory\RequestFactory::class,
AcceptListener::class => Factory\AcceptListenerFactory::class,
AcceptFilterListener::class => Factory\AcceptFilterListenerFactory::class,
ContentTypeFilterListener::class => Factory\ContentTypeFilterListenerFactory::class,
ContentNegotiationOptions::class => Factory\ContentNegotiationOptionsFactory::class,
HttpMethodOverrideListener::class => Factory\HttpMethodOverrideListenerFactory::class,
],
],
'controller_plugins' => [
'aliases' => [
'routeParam' => ControllerPlugin\RouteParam::class,
'queryParam' => ControllerPlugin\QueryParam::class,
'bodyParam' => ControllerPlugin\BodyParam::class,
'routeParams' => ControllerPlugin\RouteParams::class,
'queryParams' => ControllerPlugin\QueryParams::class,
'bodyParams' => ControllerPlugin\BodyParams::class,
],
'factories' => [
ControllerPlugin\RouteParam::class => InvokableFactory::class,
ControllerPlugin\QueryParam::class => InvokableFactory::class,
ControllerPlugin\BodyParam::class => InvokableFactory::class,
ControllerPlugin\RouteParams::class => InvokableFactory::class,
ControllerPlugin\QueryParams::class => InvokableFactory::class,
ControllerPlugin\BodyParams::class => InvokableFactory::class,
],
],
This listener is attached to the MvcEvent::EVENT_DISPATCH
event with priority -10
. It is
responsible for performing the actual selection and casting of a controller's view model based on
the content negotiation configuration.
This listener is attached to the MvcEvent::EVENT_ROUTE
event with a priority of -625
. It is
responsible for examining the Content-Type
header in order to determine how the content body
should be deserialized. Values are then persisted inside of a ParameterDataContainer
which is
stored in the ZFContentNegotiationParameterData
key of the MvcEvent
object.
This listener is attached to the MvcEvent::EVENT_ROUTE
event with a priority of -625
. It is
responsible for ensuring the controller selected by routing is configured to respond to the specific
media type in the current request's Accept
header. If it cannot, it will short-circuit the MVC
dispatch process by returning a 406 Cannot honor Accept type specified
response.
This listener is attached to the MvcEvent::EVENT_ROUTE
event with a priority of -625
. It is
responsible for ensuring the route matched controller can accept content in the request body
specified by the media type in the current request's Content-Type
header. If it cannot, it will
short-circuit the MVC dispatch process by returning a 415 Invalid content-type specified
response.
- Since 1.3.0
This listener is attached to the MvcEvent::EVENT_ROUTE
event with a priority
of -40
, but only if the x_http_method_override_enabled
configuration flag
was toggle on. It is responsible for checking if an X-HTTP-Method-Override
header is present, and, if so, if it contains a value in the set allowed for the
current HTTP request method invoked. If so, it resets the HTTP request method to
the header value.
A controller plugin (Zend\Mvc\Controller\AbstractActionController
callable) that will return a
single parameter with a particular name from the route match.
use Zend\Mvc\Controller\AbstractActionController;
class IndexController extends AbstractActionController
{
public function indexAction()
{
return $this->routeParam('id', 'someDefaultValue');
}
}
A controller plugin (Zend\Mvc\Controller\AbstractActionController
callable) that will return a
single parameter from the current request query string.
use Zend\Mvc\Controller\AbstractActionController;
class IndexController extends AbstractActionController
{
public function indexAction()
{
return $this->queryParam('foo', 'someDefaultValue');
}
}
A controller plugin (Zend\Mvc\Controller\AbstractActionController
callable) that will return a
single parameter from the content-negotiated content body.
use Zend\Mvc\Controller\AbstractActionController;
class IndexController extends AbstractActionController
{
public function indexAction()
{
return $this->bodyParam('foo', 'someDefaultValue');
}
}
A controller plugin (Zend\Mvc\Controller\AbstractActionController
callable) that will return a
all the route parameters.
use Zend\Mvc\Controller\AbstractActionController;
class IndexController extends AbstractActionController
{
public function indexAction()
{
return $this->routeParams()
}
}
A controller plugin (Zend\Mvc\Controller\AbstractActionController
callable) that will return a
all the query parameters.
use Zend\Mvc\Controller\AbstractActionController;
class IndexController extends AbstractActionController
{
public function indexAction()
{
return $this->queryParams()
}
}
A controller plugin (Zend\Mvc\Controller\AbstractActionController
callable) that will return a
all the content-negotiated body parameters.
use Zend\Mvc\Controller\AbstractActionController;
class IndexController extends AbstractActionController
{
public function indexAction()
{
return $this->bodyParams()
}
}