Skip to content

Commit 4abbfe4

Browse files
API requires accept json header (alexjustesen#2333)
Co-authored-by: Alex Justesen <[email protected]> Co-authored-by: GitHub Action <[email protected]>
1 parent 5aec7fd commit 4abbfe4

File tree

11 files changed

+388
-4
lines changed

11 files changed

+388
-4
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
namespace App\Http\Middleware;
4+
5+
use Closure;
6+
use Illuminate\Http\Request;
7+
use Illuminate\Http\Response;
8+
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
9+
10+
class AcceptJsonMiddleware
11+
{
12+
/**
13+
* Handle an incoming request.
14+
*
15+
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
16+
*/
17+
public function handle(Request $request, Closure $next): SymfonyResponse
18+
{
19+
// Check if the Accept header includes application/json
20+
if (! $request->acceptsJson()) {
21+
return response()->json([
22+
'message' => 'This endpoint only accepts JSON. Please include "Accept: application/json" in your request headers.',
23+
'error' => 'Unsupported Media Type',
24+
], Response::HTTP_NOT_ACCEPTABLE);
25+
}
26+
27+
// Ensure the response is JSON
28+
$response = $next($request);
29+
30+
// Force JSON content type if not already set
31+
if (! $response->headers->has('Content-Type') ||
32+
! str_contains($response->headers->get('Content-Type'), 'application/json')) {
33+
$response->headers->set('Content-Type', 'application/json');
34+
}
35+
36+
return $response;
37+
}
38+
}

app/OpenApi/Annotations/V1/OoklaAnnotations.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ class OoklaAnnotations
1717
description: 'Returns an array of available Ookla speedtest servers. Requires an API token with `ookla:list-servers` scope.',
1818
operationId: 'listOoklaServers',
1919
tags: ['Servers'],
20+
parameters: [
21+
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
22+
],
2023
responses: [
2124
new OA\Response(
2225
response: Response::HTTP_OK,
@@ -33,6 +36,11 @@ class OoklaAnnotations
3336
description: 'Forbidden',
3437
content: new OA\JsonContent(ref: '#/components/schemas/ForbiddenError')
3538
),
39+
new OA\Response(
40+
response: Response::HTTP_NOT_ACCEPTABLE,
41+
description: 'Not Acceptable - Missing or invalid Accept header',
42+
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
43+
),
3644
]
3745
)]
3846
public function listServers(): void {}

app/OpenApi/Annotations/V1/ResultsAnnotations.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class ResultsAnnotations
1717
operationId: 'listResults',
1818
tags: ['Results'],
1919
parameters: [
20+
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
2021
new OA\Parameter(
2122
name: 'per_page',
2223
in: 'query',
@@ -104,6 +105,11 @@ class ResultsAnnotations
104105
description: 'Unauthenticated',
105106
content: new OA\JsonContent(ref: '#/components/schemas/UnauthenticatedError')
106107
),
108+
new OA\Response(
109+
response: Response::HTTP_NOT_ACCEPTABLE,
110+
description: 'Not Acceptable - Missing or invalid Accept header',
111+
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
112+
),
107113
new OA\Response(
108114
response: Response::HTTP_UNPROCESSABLE_ENTITY,
109115
description: 'Validation failed',
@@ -119,6 +125,7 @@ public function index(): void {}
119125
operationId: 'getResult',
120126
tags: ['Results'],
121127
parameters: [
128+
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
122129
new OA\Parameter(
123130
name: 'id',
124131
in: 'path',
@@ -143,6 +150,11 @@ public function index(): void {}
143150
description: 'Unauthenticated',
144151
content: new OA\JsonContent(ref: '#/components/schemas/UnauthenticatedError')
145152
),
153+
new OA\Response(
154+
response: Response::HTTP_NOT_ACCEPTABLE,
155+
description: 'Not Acceptable - Missing or invalid Accept header',
156+
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
157+
),
146158
new OA\Response(
147159
response: Response::HTTP_NOT_FOUND,
148160
description: 'Result not found',
@@ -157,6 +169,9 @@ public function show(): void {}
157169
summary: 'Get the most recent result',
158170
operationId: 'getLatestResult',
159171
tags: ['Results'],
172+
parameters: [
173+
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
174+
],
160175
responses: [
161176
new OA\Response(
162177
response: Response::HTTP_OK,
@@ -173,6 +188,11 @@ public function show(): void {}
173188
description: 'Unauthenticated',
174189
content: new OA\JsonContent(ref: '#/components/schemas/UnauthenticatedError')
175190
),
191+
new OA\Response(
192+
response: Response::HTTP_NOT_ACCEPTABLE,
193+
description: 'Not Acceptable - Missing or invalid Accept header',
194+
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
195+
),
176196
new OA\Response(
177197
response: Response::HTTP_NOT_FOUND,
178198
description: 'No result found',

app/OpenApi/Annotations/V1/SpeedtestAnnotations.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class SpeedtestAnnotations
1717
operationId: 'runSpeedtest',
1818
tags: ['Speedtests'],
1919
parameters: [
20+
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
2021
new OA\Parameter(
2122
name: 'server_id',
2223
in: 'query',
@@ -41,6 +42,11 @@ class SpeedtestAnnotations
4142
description: 'Forbidden',
4243
content: new OA\JsonContent(ref: '#/components/schemas/ForbiddenError')
4344
),
45+
new OA\Response(
46+
response: Response::HTTP_NOT_ACCEPTABLE,
47+
description: 'Not Acceptable - Missing or invalid Accept header',
48+
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
49+
),
4450
new OA\Response(
4551
response: Response::HTTP_UNPROCESSABLE_ENTITY,
4652
description: 'Validation error',
@@ -58,6 +64,9 @@ public function run(): void
5864
summary: 'List available Ookla speedtest servers',
5965
operationId: 'listSpeedtestServers',
6066
tags: ['Speedtests'],
67+
parameters: [
68+
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
69+
],
6170
responses: [
6271
new OA\Response(
6372
response: Response::HTTP_OK,
@@ -77,6 +86,11 @@ public function run(): void
7786
example: ['message' => 'You do not have permission to view speedtest servers.']
7887
)
7988
),
89+
new OA\Response(
90+
response: Response::HTTP_NOT_ACCEPTABLE,
91+
description: 'Not Acceptable - Missing or invalid Accept header',
92+
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
93+
),
8094
]
8195
)]
8296
public function listServers(): void {}

app/OpenApi/Annotations/V1/StatsAnnotations.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class StatsAnnotations
1717
operationId: 'getStats',
1818
tags: ['Stats'],
1919
parameters: [
20+
new OA\Parameter(ref: '#/components/parameters/AcceptHeader'),
2021
new OA\Parameter(
2122
name: 'start_at',
2223
in: 'query',
@@ -48,6 +49,11 @@ class StatsAnnotations
4849
description: 'Forbidden',
4950
content: new OA\JsonContent(ref: '#/components/schemas/ForbiddenError')
5051
),
52+
new OA\Response(
53+
response: Response::HTTP_NOT_ACCEPTABLE,
54+
description: 'Not Acceptable - Missing or invalid Accept header',
55+
content: new OA\JsonContent(ref: '#/components/schemas/NotAcceptableError')
56+
),
5157
new OA\Response(
5258
response: Response::HTTP_UNPROCESSABLE_ENTITY,
5359
description: 'Validation error',

app/OpenApi/OpenApiDefinition.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
],
2121
parameters: [
2222
new OA\Parameter(
23+
parameter: 'AcceptHeader',
2324
name: 'Accept',
2425
in: 'header',
2526
required: true,
2627
schema: new OA\Schema(type: 'string', default: 'application/json'),
27-
description: 'Expected response format'
28+
description: 'Must be "application/json" - this API only accepts and returns JSON'
2829
),
2930
]
3031
),
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace App\OpenApi\Schemas;
4+
5+
use OpenApi\Attributes as OA;
6+
7+
#[OA\Schema(
8+
schema: 'NotAcceptableError',
9+
description: 'Error response when the Accept header is missing or invalid',
10+
type: 'object',
11+
properties: [
12+
new OA\Property(
13+
property: 'message',
14+
type: 'string',
15+
example: 'This endpoint only accepts JSON. Please include "Accept: application/json" in your request headers.'
16+
),
17+
new OA\Property(
18+
property: 'error',
19+
type: 'string',
20+
example: 'Unsupported Media Type'
21+
),
22+
]
23+
)]
24+
class NotAcceptableErrorSchema {}

bootstrap/app.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
$middleware->alias([
1818
'getting-started' => App\Http\Middleware\GettingStarted::class,
1919
'public-dashboard' => App\Http\Middleware\PublicDashboard::class,
20+
'accept-json' => App\Http\Middleware\AcceptJsonMiddleware::class,
2021
]);
2122

2223
$middleware->prependToGroup('api', [

0 commit comments

Comments
 (0)