Skip to content

Commit

Permalink
Add default response header support
Browse files Browse the repository at this point in the history
  • Loading branch information
shadowhand committed Oct 11, 2018
1 parent c694d94 commit 4986805
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 40 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).

## [4.2.0] 2018-10

### Added
- Allow adding default response headers to strategies.

## [4.1.1] 2018-10

### Fixed
Expand Down
69 changes: 69 additions & 0 deletions src/Strategy/AbstractStrategy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php declare(strict_types=1);

namespace League\Route\Strategy;

use Psr\Http\Message\ResponseInterface;

abstract class AbstractStrategy implements StrategyInterface
{
/** @var array */
protected $defaultResponseHeaders = [];

/**
* Get current default response headers
*
* @return array
*/
public function getDefaultResponseHeaders() : array
{
return $this->defaultResponseHeaders;
}

/**
* Add or replace a default response header
*
* @param string $name
* @param string $value
* @return static
*/
public function addDefaultResponseHeader(string $name, string $value) : AbstractStrategy
{
$this->defaultResponseHeaders[strtolower($name)] = $value;

return $this;
}

/**
* Add multiple default response headers
*
* @param array $headers
* @return static
*/
public function addDefaultResponseHeaders(array $headers) : AbstractStrategy
{
foreach ($headers as $name => $value) {
$this->addDefaultResponseHeader($name, $value);
}

return $this;
}

/**
* Apply default response headers
*
* Headers that already exist on the response will NOT be replaced.
*
* @param \Psr\Http\Message\ResponseInterface $response
* @return \Psr\Http\Message\ResponseInterface
*/
protected function applyDefaultResponseHeaders(ResponseInterface $response) : ResponseInterface
{
foreach ($this->defaultResponseHeaders as $name => $value) {
if (! $response->hasHeader($name)) {
$response = $response->withHeader($name, $value);
}
}

return $response;
}
}
8 changes: 6 additions & 2 deletions src/Strategy/ApplicationStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Psr\Http\Message\{ResponseInterface, ServerRequestInterface};
use Psr\Http\Server\{MiddlewareInterface, RequestHandlerInterface};

class ApplicationStrategy implements ContainerAwareInterface, StrategyInterface
class ApplicationStrategy extends AbstractStrategy implements ContainerAwareInterface
{
use ContainerAwareTrait;

Expand All @@ -19,7 +19,11 @@ class ApplicationStrategy implements ContainerAwareInterface, StrategyInterface
public function invokeRouteCallable(Route $route, ServerRequestInterface $request) : ResponseInterface
{
$controller = $route->getCallable($this->getContainer());
return $controller($request, $route->getVars());

$response = $controller($request, $route->getVars());
$response = $this->applyDefaultResponseHeaders($response);

return $response;
}

/**
Expand Down
11 changes: 5 additions & 6 deletions src/Strategy/JsonStrategy.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use Psr\Http\Message\{ResponseFactoryInterface, ResponseInterface, ServerRequestInterface};
use Psr\Http\Server\{MiddlewareInterface, RequestHandlerInterface};

class JsonStrategy implements ContainerAwareInterface, StrategyInterface
class JsonStrategy extends AbstractStrategy implements ContainerAwareInterface
{
use ContainerAwareTrait;

Expand All @@ -27,6 +27,8 @@ class JsonStrategy implements ContainerAwareInterface, StrategyInterface
public function __construct(ResponseFactoryInterface $responseFactory)
{
$this->responseFactory = $responseFactory;

$this->addDefaultResponseHeader('content-type', 'application/json');
}

/**
Expand All @@ -39,14 +41,11 @@ public function invokeRouteCallable(Route $route, ServerRequestInterface $reques

if ($this->isJsonEncodable($response)) {
$body = json_encode($response);
$response = $this->responseFactory->createResponse();
$response = $response->withStatus(200);
$response = $this->responseFactory->createResponse(200);
$response->getBody()->write($body);
}

if ($response instanceof ResponseInterface && ! $response->hasHeader('content-type')) {
$response = $response->withAddedHeader('content-type', 'application/json');
}
$response = $this->applyDefaultResponseHeaders($response);

return $response;
}
Expand Down
16 changes: 2 additions & 14 deletions tests/DispatchIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -711,12 +711,6 @@ public function testRouteStrategyOverridesGlobalStrategy()
->will($this->returnValue($uri))
;

$response
->expects($this->once())
->method('withStatus')
->will($this->returnSelf())
;

$response
->expects($this->once())
->method('getBody')
Expand All @@ -725,7 +719,7 @@ public function testRouteStrategyOverridesGlobalStrategy()

$response
->expects($this->once())
->method('withAddedHeader')
->method('withHeader')
->with($this->equalTo('content-type'), $this->equalTo('application/json'))
->will($this->returnSelf())
;
Expand Down Expand Up @@ -775,12 +769,6 @@ public function testRouteStrategyOverridesGroupStrategy()
->will($this->returnValue($uri))
;

$response
->expects($this->once())
->method('withStatus')
->will($this->returnSelf())
;

$response
->expects($this->once())
->method('getBody')
Expand All @@ -789,7 +777,7 @@ public function testRouteStrategyOverridesGroupStrategy()

$response
->expects($this->once())
->method('withAddedHeader')
->method('withHeader')
->with($this->equalTo('content-type'), $this->equalTo('application/json'))
->will($this->returnSelf())
;
Expand Down
34 changes: 34 additions & 0 deletions tests/Strategy/ApplicationStrategyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,40 @@

class ApplicationStrategyTest extends TestCase
{
/**
* Asserts that the strategy includes default headers.
*
* @return void
*/
public function testStrategyHasDefaultHeaders() : void
{
$strategy = new ApplicationStrategy();

$expectedHeaders = [];

$this->assertSame($expectedHeaders, $strategy->getDefaultResponseHeaders());
}

/**
* Asserts that the strategy default headers can be added to.
*
* @return void
*/
public function testStrategyCanDefineAdditionalHeaders() : void
{
$strategy = new ApplicationStrategy();

$additionalHeaders = [
'cache-control' => 'no-cache',
];

$strategy->addDefaultResponseHeaders($additionalHeaders);

$expectedHeaders = array_replace([], $additionalHeaders);

$this->assertSame($expectedHeaders, $strategy->getDefaultResponseHeaders());
}

/**
* Asserts that the strategy properly invokes the route callable
*
Expand Down
63 changes: 45 additions & 18 deletions tests/Strategy/JsonStrategyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,48 @@

class JsonStrategyTest extends TestCase
{
/**
* Asserts that the strategy includes default headers.
*
* @return void
*/
public function testStrategyHasDefaultHeaders() : void
{
$factory = $this->createMock(ResponseFactoryInterface::class);

$strategy = new JsonStrategy($factory);

$expectedHeaders = [
'content-type' => 'application/json',
];

$this->assertSame($expectedHeaders, $strategy->getDefaultResponseHeaders());
}

/**
* Asserts that the strategy default headers can be added to.
*
* @return void
*/
public function testStrategyCanDefineAdditionalHeaders() : void
{
$factory = $this->createMock(ResponseFactoryInterface::class);

$strategy = new JsonStrategy($factory);

$additionalHeaders = [
'cache-control' => 'no-cache',
];

$strategy->addDefaultResponseHeaders($additionalHeaders);

$expectedHeaders = array_replace([
'content-type' => 'application/json',
], $additionalHeaders);

$this->assertSame($expectedHeaders, $strategy->getDefaultResponseHeaders());
}

/**
* Asserts that the strategy properly invokes the route callable.
*
Expand Down Expand Up @@ -53,7 +95,7 @@ function (

$expectedResponse
->expects($this->once())
->method('withAddedHeader')
->method('withHeader')
->with($this->equalTo('content-type'), $this->equalTo('application/json'))
->will($this->returnSelf())
;
Expand Down Expand Up @@ -88,18 +130,11 @@ public function testStrategyInvokesRouteCallableWithArrayReturn() : void

$expectedResponse
->expects($this->once())
->method('withAddedHeader')
->method('withHeader')
->with($this->equalTo('content-type'), $this->equalTo('application/json'))
->will($this->returnSelf())
;

$expectedResponse
->expects($this->once())
->method('withStatus')
->with($this->equalTo(200))
->will($this->returnSelf())
;

$expectedResponse
->expects($this->once())
->method('hasHeader')
Expand Down Expand Up @@ -359,18 +394,11 @@ public function testStrategyInvokesRouteCallableWithObjectReturn() : void

$expectedResponse
->expects($this->once())
->method('withAddedHeader')
->method('withHeader')
->with($this->equalTo('content-type'), $this->equalTo('application/json'))
->will($this->returnSelf())
;

$expectedResponse
->expects($this->once())
->method('withStatus')
->with($this->equalTo(200))
->will($this->returnSelf())
;

$expectedResponse
->expects($this->once())
->method('hasHeader')
Expand Down Expand Up @@ -399,7 +427,6 @@ function (
$this->assertSame($expectedRequest, $request);
return $expectedObject;
}

))
;

Expand Down

0 comments on commit 4986805

Please sign in to comment.