8000 [Bugfix] Fix exception when Kernel terminates · jpaniorte/laravel-json-api@5b609c4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5b609c4

Browse files
committed
[Bugfix] Fix exception when Kernel terminates
Closes cloudcreativity#284
1 parent db2e67e commit 5b609c4

File tree

4 files changed

+151
-50
lines changed

4 files changed

+151
-50
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ Also added PHP 7 type-hinting to all methods in the abstract class.
2727
### Fixed
2828
- [#280](https://github.com/cloudcreativity/laravel-json-api/issues/280)
2929
Validation error objects for relationship objects now have correct source pointers.
30+
- [#284](https://github.com/cloudcreativity/laravel-json-api/issues/284)
31+
Content negotiation middleware no longer causes a container binding exception when the Kernel
32+
is terminated.
3033

3134
### Removed
3235
- The following classes in the `Validation` namespace were removed as the `Validation\Validator`

docs/upgrade.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,32 @@ return [
9898
];
9999
```
100100

101+
### Routing
102+
103+
We have made changes to the routing to introduce a fluent syntax for defining routes. This will
104+
not affect your application unless you type-hinted the `Routing\ApiGroup` class in any of your
105+
route definitions. You will now need to type-hint `Routing\RouteRegistrar` instead.
106+
107+
Change this:
108+
109+
```php
110+
use CloudCreativity\LaravelJsonApi\Routing\RouteRegistrar;
111+
112+
JsonApi::register('v1', [], function (ApiGroup $api) {
113+
// ...
114+
});
115+
```
116+
117+
to this:
118+
119+
```php
120+
use CloudCreativity\LaravelJsonApi\Routing\RouteRegistrar;
121+
122+
JsonApi::register('v1', [], function (RouteRegistrar $api) {
123+
// ...
124+
});
125+
```
126+
101127
### Controllers
102128

103129
#### Eloquent

src/Http/Middleware/NegotiateContent.php

Lines changed: 53 additions & 50 deletions
< 17AE tr class="diff-line-row">
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@
33
namespace CloudCreativity\LaravelJsonApi\Http\Middleware;
44

55
use CloudCreativity\LaravelJsonApi\Api\Api;
6+
use CloudCreativity\LaravelJsonApi\Codec\Codec;
67
use CloudCreativity\LaravelJsonApi\Codec\Decoding;
78
use CloudCreativity\LaravelJsonApi\Codec\Encoding;
89
use CloudCreativity\LaravelJsonApi\Contracts\ContainerInterface;
910
use CloudCreativity\LaravelJsonApi\Contracts\Http\ContentNegotiatorInterface;
1011
use CloudCreativity\LaravelJsonApi\Exceptions\DocumentRequiredException;
1112
use CloudCreativity\LaravelJsonApi\Factories\Factory;
1213
use CloudCreativity\LaravelJsonApi\Routing\Route;
14+
use Illuminate\Contracts\Container\Container;
1315
use Illuminate\Http\Request;
16+
use Neomerx\JsonApi\Contracts\Http\Headers\AcceptHeaderInterface;
17+
use Neomerx\JsonApi\Contracts\Http\Headers\HeaderInterface;
1418
use Neomerx\JsonApi\Contracts\Http\Headers\HeaderParametersInterface;
1519
use Symfony\Component\HttpKernel\Exception\HttpException;
16-
use function CloudCreativity\LaravelJsonApi\http_contains_body;
1720

1821
/**
1922
* Class NegotiateContent
@@ -24,19 +27,14 @@ class NegotiateContent
2427
{
2528

2629
/**
27-
* @var Factory
28-
*/
29-
private $factory;
30-
31-
/**
32-
* @var Api
30+
* @var Container
3331
*/
34-
private $api;
32+
private $container;
3533

3634
/**
37-
* @var HeaderParametersInterface
35+
* @var Factory
3836
*/
39-
private $headers;
37+
private $factory;
4038

4139
/**
4240
* @var Route
@@ -46,16 +44,14 @@ class NegotiateContent
4644
/**
4745
* NegotiateContent constructor.
4846
*
47+
* @param Container $container
4948
* @param Factory $factory
50-
* @param Api $api
51-
* @param HeaderParametersInterface $headers
5249
* @param Route $route
5350
*/
54-
public function __construct(Factory $factory, Api $api, HeaderParametersInterface $headers, Route $route)
51+
public function __construct(Container $container, Factory $factory, Route $route)
5552
{
53+
$this->container = $container;
5654
$this->factory = $factory;
57-
$this->api = $api;
58-
$this->headers = $headers;
5955
$this->route = $route;
6056
}
6157

@@ -71,33 +67,44 @@ public function __construct(Factory $factory, Api $api, HeaderParametersInterfac
7167
*/
7268
public function handle($request, \Closure $next, string $default = null)
7369
{
74-
$body = http_contains_body($request);
70+
$api = $this->container->make(Api::class);
71+
/** @var HeaderParametersInterface $headers */
72+
$headers = $this->container->make(HeaderParametersInterface::class);
73+
$contentType = $headers->getContentTypeHeader();
7574

76-
$this->matched(
77-
$this->matchEncoding($request, $default),
78-
$decoder = $body ? $this->matchDecoder($request, $default) : null
75+
$codec = $this->factory->createCodec(
76+
$api->getContainer(),
77+
$this->matchEncoding($api, $request, $headers->getAcceptHeader(), $default),
78+
$decoder = $contentType ? $this->matchDecoder($api, $request, $contentType, $default) : null
7979
);
8080

81-
if (!$body && $this->isExpectingContent($request)) {
81+
$this->matched($codec);
82+
83+
if (!$contentType && $this->isExpectingContent($request)) {
8284
throw new DocumentRequiredException();
8385
}
8486

8587
return $next($request);
8688
}
8789

8890
/**
91+
* @param Api $api
8992
* @param Request $request
93+
* @param AcceptHeaderInterface $accept
9094
* @param string|null $defaultNegotiator
9195
* @return Encoding
9296
*/
93-
protected function matchEncoding($request, ?string $defaultNegotiator): Encoding
97+
protected function matchEncoding(
98+
Api $api,
99+
$request,
100+
AcceptHeaderInterface $accept,
101+
?string $defaultNegotiator
102+
): Encoding
94103
{
95104
$negotiator = $this
96-
->negotiator($this->responseResourceType(), $defaultNegotiator)
105+
->negotiator($api->getContainer(), $this->responseResourceType(), $defaultNegotiator)
97106
->withRequest($request)
98-
->withApi($this->api);
99-
100-
$accept = $this->headers->getAcceptHeader();
107+
->withApi($api);
101108

102109
if ($this->willSeeMany($request)) {
103110
return $negotiator->encodingForMany($accept);
@@ -107,18 +114,24 @@ protected function matchEncoding($request, ?string $defaultNegotiator): Encoding
107114
}
108115

109116
/**
117+
* @param Api $api
110118
* @param Request $request
119+
* @param HeaderInterface $contentType
111120
* @param string|null $defaultNegotiator
112121
* @return Decoding|null
113122
*/
114-
protected function matchDecoder($request, ?string $defaultNegotiator): ?Decoding
123+
protected function matchDecoder(
124+
Api $api,
125+
$request,
126+
HeaderInterface $contentType,
127+
?string $defaultNegotiator
128+
): ?Decoding
115129
{
116130
$negotiator = $this
117-
->negotiator($this->route->getResourceType(), $defaultNegotiator)
131+
->negotiator($api->getContainer(), $this->route->getResourceType(), $defaultNegotiator)
118132
->withRequest($request)
119-
->withApi($this->api);
133+
->withApi($api);
120134

121-
$contentType = $this->headers->getContentTypeHeader();
122135
$resource = $this->route->getResource();
123136

124137
if ($resource && $field = $this->route->getRelationshipName()) {
@@ -139,18 +152,23 @@ protected function responseResourceType(): ?string
139152
}
140153

141154
/**
155+
* @param ContainerInterface $container
142156
* @param string|null $resourceType
143157
* @param string|null $default
144158
* @return ContentNegotiatorInterface
145159
*/
146-
protected function negotiator(?string $resourceType, ?string $default): ContentNegotiatorInterface
160+
protected function negotiator(
161+
ContainerInterface $container,
162+
?string $resourceType,
163+
?string $default
164+
): ContentNegotiatorInterface
147165
{
148-
if ($resourceType && $negotiator = $this->getContainer()->getContentNegotiatorByResourceType($resourceType)) {
166+
if ($resourceType && $negotiator = $container->getContentNegotiatorByResourceType($resourceType)) {
149167
return $negotiator;
150168
}
151169

152170
if ($default) {
153-
return $this->getContainer()->getContentNegotiatorByName($default);
171+
return $container->getContentNegotiatorByName($default);
154172
}
155173

156174
return $this->defaultNegotiator();
@@ -167,30 +185,15 @@ protected function defaultNegotiator(): ContentNegotiatorInterface
167185
}
168186

169187
/**
170-
* Apply the matched encoding and decoding.
188+
* Apply the matched codec.
171189
*
172-
* @param Encoding $encoding
173-
* @param Decoding|null $decoding
190+
* @param Codec $codec
174191
*/
175-
protected function matched(Encoding $encoding, ?Decoding $decoding): void
192+
protected function matched(Codec $codec): void
176193
{
177-
$codec = $this->factory->createCodec(
178-
$this->getContainer(),
179-
$encoding,
180-
$decoding
181-
);
182-
183194
$this->route->setCodec($codec);
184195
}
185196

186-
/**
187-
* @return ContainerInterface
188-
*/
189-
protected function getContainer(): ContainerInterface
190-
{
191-
return $this->api->getContainer();
192-
}
193-
194197
/**
195198
* Will the response contain a specific resource?
196199
*
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
/**
3+
* Copyright 2019 Cloud Creativity Limited
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
namespace CloudCreativity\LaravelJsonApi\Tests\Integration\Auth;
19+
20+
use CloudCreativity\LaravelJsonApi\Facades\JsonApi;
21+
use CloudCreativity\LaravelJsonApi\Routing\RouteRegistrar;
22+
use CloudCreativity\LaravelJsonApi\Tests\Integration\TestCase;
23+
use Illuminate\Support\Facades\Route;
24+
25+
class Issue284Test extends TestCase
26+
{
27+
28+
/**
29+
* @var bool
30+
*/
31+
protected $appRoutes = false;
32+
33+
/**
34+
* Test authorization exception *before* JSON API middleware.
35+
*/
36+
public function test()
37+
{
38+
Route::group([
39+
'namespace' => '\\DummyApp\\Http\\Controllers',
40+
'middleware' => 'auth'
41+
], function () {
42+
JsonApi::register('v1', [], function (RouteRegistrar $api) {
43+
$api->resource('posts');
44+
});
45+
});
46+
47+
$this->getJsonApi('/api/v1/posts')->assertErrorStatus([
48+
'status' => '401',
49+
'title' => 'Unauthenticated',
50+
]);
51+
}
52+
53+
public function testFluent()
54+
{
55+
Route::group([
56+
'namespace' => '\\DummyApp\\Http\\Controllers',
57+
'middleware' => 'auth'
58+
], function () {
59+
JsonApi::register('v1')->routes(function (RouteRegistrar $api) {
60+
$api->resource('posts');
61+
});
62+
});
63+
64+
$this->getJsonApi('/api/v1/posts')->assertErrorStatus([
65+
'status' => '401',
66+
'title' => 'Unauthenticated',
67+
]);
68+
}
69+
}

0 commit comments

Comments
 (0)
0