Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

dingo_d's avatar

When running json api tests content-type header is ignored?

I'm using this library to create my API, and I added a 'login' custom route - this route isn't a resource one so I added a separate controller for it

JsonApi::register('v1')->routes(static function (Api $api) {
    /**
     * Custom route that uses SessionController
     *
     * Used for 'log in' purposes (bearer token generation), and
     * 'log out' purposes (revoking bearer token).
     */
    $api->post('login', [SessionController::class, 'login'])
        ->name('login')
        ->middleware('throttle:10,1');

// Other routes are registered here.
});

I have a login() method in my SessionController (which extends the JsonApiController). When I test it in postman, everything works.

I wrote a test for it

<?php

namespace Tests\Feature\Api;

use Carbon\Carbon;
use Laravel\Passport\Passport;
use Tests\UsersTestCase;
use App\User;

class LoginTest extends UsersTestCase
{
    /**
     * @var string
     */
    protected $loginRoute = 'api/v1/login';

    /**
     * @var array
     */
    protected $headers = [
        'Accept' => 'application/vnd.api+json',
        'Content-Type' => 'application/vnd.api+json'
    ];

    /**
     * @return void
     */
    public function setUp(): void
    {
        parent::setUp();

        $this->artisan('passport:install');
    }

    public function testApprovedUserCanLogIn()
    {
        // Set up user.
        $password = 'asd81ikj3$adfg2.';

        $user = factory(User::class)->create([
            'approved_at' => Carbon::now()->format('Y-m-d H:i:s'),
            'password' => bcrypt($password),
        ]);

        $user->assignRole('admin');

        $data = [
            'email' => $user->email,
            'password' => $password,
            'remember_me' => true,
        ];

        Passport::actingAs($user);

        $response = $this
            ->withHeaders($this->headers)
            ->postJson($this->loginRoute, $data, $this->headers);

        $response
            ->assertStatus(200)
            ->assertJsonStructure([
                'type',
                'attributes' => [
                    'access_token',
                    'token_type',
                    'expires_at',
                ]
            ]);
    }
}

And when I run it, I get

{#2411
  +"errors": array:1 [
    0 => {#2539
      +"status": "415"
      +"title": "Unsupported Media Type"
      +"detail": "The request entity has a media type which the server or resource does not support."
    }
  ]
}

So I dug a bit deeper into Laravel's postJson method and it turns out that there are headers sent in this way:

    /**
     * Call the given URI with a JSON request.
     *
     * @param  string  $method
     * @param  string  $uri
     * @param  array  $data
     * @param  array  $headers
     * @return \Illuminate\Foundation\Testing\TestResponse
     */
    public function json($method, $uri, array $data = [], array $headers = [])
    {
        $files = $this->extractFilesFromDataArray($data);

        $content = json_encode($data);

        $headers = array_merge([
            'CONTENT_LENGTH' => mb_strlen($content, '8bit'),
            'CONTENT_TYPE' => 'application/json',
            'Accept' => 'application/json',
        ], $headers);

        error_log(print_r($headers, true));

        return $this->call(
            $method, $uri, [], [], $files, $this->transformHeadersToServerVars($headers), $content
        );
    }

So it looks like my Content-Type header is ignored completely in favor of CONTENT_TYPE. When I tried setting CONTENT_TYPE in my test I got an error

testing.ERROR: Method Illuminate\Auth\RequestGuard::attempt does not exist. {"userId":1,"exception":"[object] (BadMethodCallException(code: 0): Method Illuminate\Auth\RequestGuard::attempt does not exist. at /Users/infinum-denis/my-test-api/vendor/laravel/framework/src/Illuminate/Support/Traits/Macroable.php:103) [stacktrace]

So I'm a bit lost here.

Are the tests forcing application/json content type? How can I overwrite it? Because the error from the test happens only if I don't specify the application/vnd.api+json content type during my API calls (even in postman).

0 likes
0 replies

Please or to participate in this conversation.