Skip to content

Commit

Permalink
replaced detect_format with format_priorities implementing more sophi…
Browse files Browse the repository at this point in the history
…sticated Accept header negotiations (however it seems that the request doesnt take into account the detected format)
  • Loading branch information
lsmith77 committed Apr 27, 2011
1 parent 10cc2b0 commit 02916d4
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 37 deletions.
10 changes: 7 additions & 3 deletions DependencyInjection/Configuration.php
Expand Up @@ -72,10 +72,14 @@ public function getConfigTreeBuilder()
->end()
->end()
->arrayNode('format_listener')
->treatTrueLike(array('detect_format' => true, 'default_format' => 'html', 'decode_body' => true))
->canBeUnset()
->fixXmlConfig('format_priority', 'format_priorities')
->treatTrueLike(array('format_priorities' => array('html' => 1), 'default_format' => 'html', 'decode_body' => true))
->children()
->scalarNode('detect_format')->defaultFalse()->end()
->arrayNode('format_priorities')
->treatTrueLike(array('html' => 1))
->useAttributeAsKey('name')
->prototype('scalar')->end()
->end()
->scalarNode('default_format')->defaultValue('html')->end()
->scalarNode('decode_body')->defaultFalse()->end()
->end()
Expand Down
18 changes: 13 additions & 5 deletions DependencyInjection/FOSRestExtension.php
Expand Up @@ -78,13 +78,21 @@ public function load(array $configs, ContainerBuilder $container)

if (!empty($config['format_listener'])) {
$loader->load('request_format_listener.xml');
$container->setParameter($this->getAlias().'.detect_format', $config['format_listener']['detect_format']);
if ($config['format_listener']['detect_format']) {
$container->getDefinition('fos_rest.request_format_listener')
->replaceArgument(3, new Reference('fos_rest.serializer'));
}
$container->setParameter($this->getAlias().'.format_priorities', $config['format_listener']['format_priorities']);
$container->setParameter($this->getAlias().'.decode_body', $config['format_listener']['decode_body']);
$container->setParameter($this->getAlias().'.default_format', $config['format_listener']['default_format']);

if (!empty($config['format_listener']['format_priorities'])) {
$container->getDefinition('fos_rest.request_format_listener')
->addMethodCall('setSerializer', array(new Reference('fos_rest.serializer')));
}

if (!empty($config['format_listener']['format_priorities'])
|| $config['format_listener']['default_format']
) {
$container->getDefinition('fos_rest.request_format_listener')
->addMethodCall('setRouter', array(new Reference('router')));
}
}

if (!empty($config['frameworkextra'])) {
Expand Down
11 changes: 7 additions & 4 deletions README.md
Expand Up @@ -99,16 +99,19 @@ To enable the request listener simply adapt your configuration as follows:
fos_rest:
format_listener: true

In the behavior of the request listener can be configured in a more granular fashion:
In the behavior of the request listener can be configured in a more granular fashion.
Below you can see the defaults in case ``format_listener`` is set to true as above:

# app/config.yml
fos_rest:
format_listener:
detect_format: true
format_priorities:
html: 1
decode_body: true
default_format: json
default_format: html

Note in case for example more complex Accept header negotiations are required, the user
Note that setting ``format_priorities`` to a non empty array enables Accept header negotiations.
Alsos note in case for example more complex Accept header negotiations are required, the user
should either set a custom RequestListener class or register their own "onCoreRequest" event.

# app/config.yml
Expand Down
100 changes: 77 additions & 23 deletions Request/RequestListener.php
Expand Up @@ -5,7 +5,8 @@
use Symfony\Component\HttpFoundation\ParameterBag,
Symfony\Component\HttpKernel\Event\GetResponseEvent,
Symfony\Component\Serializer\SerializerInterface,
Symfony\Component\Serializer\Encoder\DecoderInterface;
Symfony\Component\Serializer\Encoder\DecoderInterface,
Symfony\Component\Routing\RouterInterface;

/*
* This file is part of the FOSRestBundle
Expand All @@ -26,9 +27,9 @@
class RequestListener
{
/**
* @var Boolean if to try and detect the request format
* @param array $formatPriorities Key format, value priority (empty array means no Accept header matching)
*/
protected $detectFormat;
protected $formatPriorities;

/**
* @param string default format name
Expand All @@ -45,22 +46,45 @@ class RequestListener
*/
protected $serializer;

/**
* @var RouterInterface
*/
protected $router;
/**
* Initialize RequestListener.
*
* @param Boolean $detectFormat If to try and detect the format
* @param string $defaultFormat Default fallback format
* @param Boolean $decodeBody If to decode the body for parameters
* @param SerializerInterface $serializer A serializer instance with all relevant encoders (lazy) loaded
* @param array $formatPriorities Key format, value priority (empty array means no Accept header matching)
* @param string $defaultFormat Default fallback format
* @param Boolean $decodeBody If to decode the body for parameters
*/
public function __construct($detectFormat, $defaultFormat, $decodeBody, SerializerInterface $serializer = null)
public function __construct($formatPriorities, $defaultFormat, $decodeBody)
{
$this->detectFormat = $detectFormat;
$this->formatPriorities = $formatPriorities;
$this->defaultFormat = $defaultFormat;
$this->decodeBody = $decodeBody;
}

/**
* Set a serializer instance
*
* @param SerializerInterface $serializer A serializer instance with all relevant encoders (lazy) loaded
*/
public function setSerializer(SerializerInterface $serializer)
{
$this->serializer = $serializer;
}


/**
* Set a router instance
*
* @param RouterInterface $router A router instance
*/
public function setRouter(RouterInterface $router = null)
{
$this->router = $router;
}

/**
* Core request handler
*
Expand All @@ -70,36 +94,52 @@ public function onCoreRequest(GetResponseEvent $event)
{
$request = $event->getRequest();

if ($this->detectFormat) {
$this->detectFormat($request);
} elseif (null !== $this->defaultFormat && null === $request->getRequestFormat(null)) {
$request->setRequestFormat($this->defaultFormat);
if ($this->router) {
if ($this->serializer && !empty($this->formatPriorities)) {
$this->detectFormat($request, $this->formatPriorities);
} elseif (null !== $this->defaultFormat && null === $request->get('_format')) {
$this->setFormat($request, $this->defaultFormat);
}
}

if ($this->decodeBody) {
$this->decodeBody($request);
}
}

/**
* Set the format on the router and request
*
* @param Request $request The request
* @param string $format The format
*/
protected function setFormat($request, $format)
{
$context = $this->router->getContext();
$context->setParameter('_format', $format);
$request->setRequestFormat($format);
}

/**
* Detect the request format in the following order:
*
* - Request
* - Accept Header
* - Default
*
* @param Request $request The request
* @param Request $request The request
* @param array $formatPriorities Key format, value priority
*/
protected function detectFormat($request)
protected function detectFormat($request, $priorities)
{
$format = $request->getRequestFormat(null);
$format = $request->get('_format');
if (null === $format) {
$format = $this->getFormatFromAcceptHeader($request);
$format = $this->getFormatFromAcceptHeader($request, $priorities);
if (null === $format) {
$format = $this->defaultFormat;
}

$request->setRequestFormat($format);
$this->setFormat($request, $format);
}
}

Expand All @@ -109,17 +149,31 @@ protected function detectFormat($request)
* Override this method to implement more complex Accept header negotiations
*
* @param Request $request The request
* @param array $formatPriorities Key format, value priority
*
* @return void|string The format string
*/
protected function getFormatFromAcceptHeader($request)
protected function getFormatFromAcceptHeader($request, $priorities)
{
$formats = $request->splitHttpAcceptHeader($request->headers->get('Accept'));
if (empty($formats)) {
$mimetypes = $request->splitHttpAcceptHeader($request->headers->get('Accept'));
if (empty($mimetypes)) {
return null;
}

$format = key($formats);
return $request->getFormat($format);
$max = reset($mimetypes);
$keys = array_keys($mimetypes, $max);
$formats = array();
foreach ($keys as $mimetype) {
$format = $request->getFormat($mimetype);
if (empty($formats[$format])) {
$formats[$format] = $max + (isset($priorities[$format]) ? $priorities[$format] : 0);
}
}

arsort($formats);
reset($formats);

return key($formats);
}

/**
Expand Down
3 changes: 1 addition & 2 deletions Resources/config/request_format_listener.xml
Expand Up @@ -8,10 +8,9 @@

<service id="fos_rest.request_format_listener" class="%fos_rest.request_format_listener.class%">
<tag name="kernel.listener" event="onCoreRequest" />
<argument>%fos_rest.detect_format%</argument>
<argument>%fos_rest.format_priorities%</argument>
<argument>%fos_rest.default_format%</argument>
<argument>%fos_rest.decode_body%</argument>
<argument>null</argument>
</service>

</services>
Expand Down

0 comments on commit 02916d4

Please sign in to comment.