Skip to content

Commit 11bc151

Browse files
committed
Add a Controller function to make it easy to return json
If the serializer component is enabled it is used to generate the json data, if not the standard `json_encode` function is used
1 parent 83b53f4 commit 11bc151

File tree

4 files changed

+117
-21
lines changed

4 files changed

+117
-21
lines changed

src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Controller;
1313

14+
use Symfony\Bundle\FrameworkBundle\Response\JsonSerializedResponse;
1415
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
1516
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
17+
use Symfony\Component\HttpFoundation\JsonResponse;
1618
use Symfony\Component\HttpFoundation\Response;
1719
use Symfony\Component\HttpFoundation\RedirectResponse;
1820
use Symfony\Component\HttpFoundation\StreamedResponse;
@@ -97,6 +99,28 @@ protected function redirectToRoute($route, array $parameters = array(), $status
9799
return $this->redirect($this->generateUrl($route, $parameters), $status);
98100
}
99101

102+
/**
103+
* Returns a JsonResponse that uses the serializer component if enabled, or json_encode.
104+
*
105+
* @param mixed $data The response data
106+
* @param int $status The status code to use for the Response
107+
* @param array $headers Array of extra headers to add
108+
* @param array $context Context to pass to serializer when using serializer component
109+
*
110+
* @return JsonResponse
111+
*/
112+
protected function json($data, $status = 200, $headers = array(), $context = array())
113+
{
114+
if ($this->container->has('serializer')) {
115+
$json = $this->container->get('serializer')->serialize($data, 'json', array_merge($context, array(
116+
'json_encode_options' => JsonResponse::DEFAULT_ENCODING_OPTIONS
117+
)));
118+
return new JsonResponse($json, $status, $headers, true);
119+
}
120+
121+
return new JsonResponse($data, $status, $headers);
122+
}
123+
100124
/**
101125
* Adds a flash message to the current session for type.
102126
*

src/Symfony/Bundle/FrameworkBundle/Tests/Controller/ControllerTest.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,18 @@
1111

1212
namespace Symfony\Bundle\FrameworkBundle\Tests\Controller;
1313

14+
use Symfony\Bundle\FrameworkBundle\Response\JsonSerializedResponse;
1415
use Symfony\Bundle\FrameworkBundle\Tests\TestCase;
1516
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
1617
use Symfony\Component\DependencyInjection\ContainerInterface;
18+
use Symfony\Component\HttpFoundation\JsonResponse;
1719
use Symfony\Component\HttpFoundation\Request;
1820
use Symfony\Component\HttpFoundation\RequestStack;
1921
use Symfony\Component\HttpFoundation\Response;
2022
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
2123
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
2224
use Symfony\Component\Security\Core\User\User;
25+
use Symfony\Component\Serializer\SerializerInterface;
2326

2427
class ControllerTest extends TestCase
2528
{
@@ -124,6 +127,52 @@ private function getContainerWithTokenStorage($token = null)
124127

125128
return $container;
126129
}
130+
131+
public function testJson()
132+
{
133+
$container = $this->getMock(ContainerInterface::class);
134+
$container
135+
->expects($this->once())
136+
->method('has')
137+
->with('serializer')
138+
->will($this->returnValue(false));
139+
140+
$controller = new TestController();
141+
$controller->setContainer($container);
142+
143+
$response = $controller->json(array());
144+
$this->assertInstanceOf(JsonResponse::class, $response);
145+
$this->assertEquals('[]', $response->getContent());
146+
}
147+
148+
public function testJsonWithSerializer()
149+
{
150+
$container = $this->getMock(ContainerInterface::class);
151+
$container
152+
->expects($this->once())
153+
->method('has')
154+
->with('serializer')
155+
->will($this->returnValue(true));
156+
157+
$serializer = $this->getMock(SerializerInterface::class);
158+
$serializer
159+
->expects($this->once())
160+
->method('serialize')
161+
->will($this->returnValue('[]'));
162+
163+
$container
164+
->expects($this->once())
165+
->method('get')
166+
->with('serializer')
167+
->will($this->returnValue($serializer));
168+
169+
$controller = new TestController();
170+
$controller->setContainer($container);
171+
172+
$response = $controller->json(array());
173+
$this->assertInstanceOf(JsonResponse::class, $response);
174+
$this->assertEquals('[]', $response->getContent());
175+
}
127176
}
128177

129178
class TestController extends Controller
@@ -137,4 +186,9 @@ public function getUser()
137186
{
138187
return parent::getUser();
139188
}
189+
190+
public function json($data, $status = 200, $headers = array(), $context = array())
191+
{
192+
return parent::json($data, $status, $headers, $context);
193+
}
140194
}

src/Symfony/Component/HttpFoundation/JsonResponse.php

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,27 @@ class JsonResponse extends Response
2929

3030
// Encode <, >, ', &, and " for RFC4627-compliant JSON, which may also be embedded into HTML.
3131
// 15 === JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
32-
protected $encodingOptions = 15;
32+
const DEFAULT_ENCODING_OPTIONS = 15;
33+
34+
protected $encodingOptions = self::DEFAULT_ENCODING_OPTIONS;
3335

3436
/**
3537
* Constructor.
3638
*
3739
* @param mixed $data The response data
3840
* @param int $status The response status code
3941
* @param array $headers An array of response headers
42+
* @param boolean $preEncoded If the data is already a JSON string
4043
*/
41-
public function __construct($data = null, $status = 200, $headers = array())
44+
public function __construct($data = null, $status = 200, $headers = array(), $preEncoded = false)
4245
{
4346
parent::__construct('', $status, $headers);
4447

4548
if (null === $data) {
4649
$data = new \ArrayObject();
4750
}
4851

49-
$this->setData($data);
52+
$this->setData($data, $preEncoded);
5053
}
5154

5255
/**
@@ -88,34 +91,37 @@ public function setCallback($callback = null)
8891
* Sets the data to be sent as JSON.
8992
*
9093
* @param mixed $data
94+
* @param boolean $preEncoded If the data is already a JSON string
9195
*
9296
* @return JsonResponse
9397
*
9498
* @throws \InvalidArgumentException
9599
*/
96-
public function setData($data = array())
100+
public function setData($data = array(), $preEncoded = false)
97101
{
98-
if (defined('HHVM_VERSION')) {
99-
// HHVM does not trigger any warnings and let exceptions
100-
// thrown from a JsonSerializable object pass through.
101-
// If only PHP did the same...
102-
$data = json_encode($data, $this->encodingOptions);
103-
} else {
104-
try {
105-
// PHP 5.4 and up wrap exceptions thrown by JsonSerializable
106-
// objects in a new exception that needs to be removed.
107-
// Fortunately, PHP 5.5 and up do not trigger any warning anymore.
102+
if (!$preEncoded) {
103+
if (defined('HHVM_VERSION')) {
104+
// HHVM does not trigger any warnings and let exceptions
105+
// thrown from a JsonSerializable object pass through.
106+
// If only PHP did the same...
108107
$data = json_encode($data, $this->encodingOptions);
109-
} catch (\Exception $e) {
110-
if ('Exception' === get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
111-
throw $e->getPrevious() ?: $e;
108+
} else {
109+
try {
110+
// PHP 5.4 and up wrap exceptions thrown by JsonSerializable
111+
// objects in a new exception that needs to be removed.
112+
// Fortunately, PHP 5.5 and up do not trigger any warning anymore.
113+
$data = json_encode($data, $this->encodingOptions);
114+
} catch (\Exception $e) {
115+
if ('Exception' === get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
116+
throw $e->getPrevious() ?: $e;
117+
}
118+
throw $e;
112119
}
113-
throw $e;
114120
}
115-
}
116121

117-
if (JSON_ERROR_NONE !== json_last_error()) {
118-
throw new \InvalidArgumentException(json_last_error_msg());
122+
if (JSON_ERROR_NONE !== json_last_error()) {
123+
throw new \InvalidArgumentException(json_last_error_msg());
124+
}
119125
}
120126

121127
$this->data = $data;

src/Symfony/Component/HttpFoundation/Tests/JsonResponseTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,18 @@ public function testConstructorWithCustomContentType()
7575
$this->assertSame('application/vnd.acme.blog-v1+json', $response->headers->get('Content-Type'));
7676
}
7777

78+
public function testConstructorWithPreEncoded()
79+
{
80+
$response = new JsonResponse('1', 200, array(), true);
81+
$this->assertEquals('1', $response->getContent());
82+
83+
$response = new JsonResponse('[1]', 200, array(), true);
84+
$this->assertEquals('[1]', $response->getContent());
85+
86+
$response = new JsonResponse('true', 200, array(), true);
87+
$this->assertEquals('true', $response->getContent());
88+
}
89+
7890
public function testCreate()
7991
{
8092
$response = JsonResponse::create(array('foo' => 'bar'), 204);

0 commit comments

Comments
 (0)