Skip to content

Commit aa32fbe

Browse files
committed
Merge application response headers
- Compose the application response into the SendApiProblemResponseListener when it is an HTTP response. - When calling `sendHeaders()`, merge in the application response headers into the ApiProblemResponse headers to ensure things such as CORS support will function.
1 parent 9707627 commit aa32fbe

File tree

4 files changed

+81
-1
lines changed

4 files changed

+81
-1
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"require": {
2929
"php": ">=5.3.23",
3030
"zendframework/zend-eventmanager": "~2.3",
31+
"zendframework/zend-http": "~2.3",
3132
"zendframework/zend-json": "~2.3",
3233
"zendframework/zend-mvc": "~2.3",
3334
"zendframework/zend-view": "~2.3"
@@ -37,7 +38,6 @@
3738
"phpunit/PHPUnit": "3.7.*",
3839
"satooshi/php-coveralls": ">=0.6.0",
3940
"zendframework/zend-console": "~2.3",
40-
"zendframework/zend-http": "~2.3",
4141
"zendframework/zend-loader": "~2.3"
4242
},
4343
"autoload": {

src/Factory/SendApiProblemResponseListenerFactory.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
namespace ZF\ApiProblem\Factory;
88

9+
use Zend\Http\Response as HttpResponse;
910
use Zend\ServiceManager\FactoryInterface;
1011
use Zend\ServiceManager\ServiceLocatorInterface;
1112
use ZF\ApiProblem\Listener\SendApiProblemResponseListener;
@@ -26,6 +27,13 @@ public function createService(ServiceLocatorInterface $serviceLocator)
2627
$listener = new SendApiProblemResponseListener();
2728
$listener->setDisplayExceptions($displayExceptions);
2829

30+
if ($serviceLocator->has('Response')) {
31+
$response = $serviceLocator->get('Response');
32+
if ($response instanceof HttpResponse) {
33+
$listener->setApplicationResponse($response);
34+
}
35+
}
36+
2937
return $listener;
3038
}
3139
}

src/Listener/SendApiProblemResponseListener.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
namespace ZF\ApiProblem\Listener;
88

9+
use Zend\Http\Response as HttpResponse;
910
use Zend\Mvc\ResponseSender\HttpResponseSender;
1011
use Zend\Mvc\ResponseSender\SendResponseEvent;
1112
use ZF\ApiProblem\ApiProblemResponse;
@@ -15,11 +16,26 @@
1516
*/
1617
class SendApiProblemResponseListener extends HttpResponseSender
1718
{
19+
/**
20+
* @var HttpResponse;
21+
*/
22+
protected $applicationResponse;
23+
1824
/**
1925
* @var bool
2026
*/
2127
protected $displayExceptions = false;
2228

29+
/**
30+
* @param HttpResponse $response
31+
* @return self
32+
*/
33+
public function setApplicationResponse(HttpResponse $response)
34+
{
35+
$this->applicationResponse = $response;
36+
return $this;
37+
}
38+
2339
/**
2440
* Set the flag determining whether exception stack traces are included
2541
*
@@ -61,6 +77,29 @@ public function sendContent(SendResponseEvent $e)
6177
return parent::sendContent($e);
6278
}
6379

80+
/**
81+
* Send HTTP response headers
82+
*
83+
* If an application response is composed, and is an HTTP response, merges
84+
* its headers with the ApiProblemResponse headers prior to sending them.
85+
*
86+
* @param SendResponseEvent $e
87+
* @return self
88+
*/
89+
public function sendHeaders(SendResponseEvent $e)
90+
{
91+
$response = $e->getResponse();
92+
if (!$response instanceof ApiProblemResponse) {
93+
return $this;
94+
}
95+
96+
if ($this->applicationResponse instanceof HttpResponse) {
97+
$this->mergeHeaders($this->applicationResponse, $response);
98+
}
99+
100+
return parent::sendHeaders($e);
101+
}
102+
64103
/**
65104
* Send ApiProblem response
66105
*
@@ -79,4 +118,21 @@ public function __invoke(SendResponseEvent $event)
79118
$event->stopPropagation(true);
80119
return $this;
81120
}
121+
122+
/**
123+
* Merge headers set on the application response into the API Problem response
124+
*
125+
* @param HttpResponse $applicationResponse
126+
* @param ApiProblemResponse $apiProblemResponse
127+
*/
128+
protected function mergeHeaders(HttpResponse $applicationResponse, ApiProblemResponse $apiProblemResponse)
129+
{
130+
$apiProblemHeaders = $apiProblemResponse->getHeaders();
131+
foreach ($applicationResponse->getHeaders() as $header) {
132+
if ($apiProblemHeaders->has($header->getFieldName())) {
133+
continue;
134+
}
135+
$apiProblemHeaders->addHeader($header);
136+
}
137+
}
82138
}

test/Listener/SendApiProblemResponseListenerTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,20 @@ public function testSendContentDoesNothingIfEventDoesNotContainApiProblemRespons
8181
$this->assertInternalType('string', $contents);
8282
$this->assertEmpty($contents);
8383
}
84+
85+
public function testSendHeadersMergesApplicationAndProblemHttpHeaders()
86+
{
87+
$appResponse = new HttpResponse();
88+
$appResponse->getHeaders()->addHeaderLine('Access-Control-Allow-Origin', '*');
89+
90+
$listener = new SendApiProblemResponseListener();
91+
$listener->setApplicationResponse($appResponse);
92+
93+
ob_start();
94+
$listener->sendHeaders($this->event);
95+
ob_get_clean();
96+
97+
$headers = $this->response->getHeaders();
98+
$this->assertTrue($headers->has('Access-Control-Allow-Origin'));
99+
}
84100
}

0 commit comments

Comments
 (0)