cloudcreativity / laravel-json-api

JSON API (jsonapi.org) package for Laravel applications.
http://laravel-json-api.readthedocs.io/en/latest/
Apache License 2.0
780 stars 109 forks source link

Returning custom media-type fails #531

Closed gaaf closed 4 years ago

gaaf commented 4 years ago

Hi,

I'm trying to optionally send a text/csv response instead of application/vnd.api+json for some endpoint, but it always fails with the message: Codec does not support encoding JSON API content.

I added text/csv to the config:

    'encoding' => [
        'application/vnd.api+json' => JSON_PRETTY_PRINT,
        'text/csv' => false,
    ],

Implemented the reading hook in the controller, similar to the test/dummy app's avatar endpoint:

    protected function reading(Region $region) : ?\Illuminate\Http\Response
    {
        \Log::debug('reading');
        if ($this->willEncode('text/csv')) {
            \Log::debug('csv');
            return $this->reply()->content("col1, col2\n")->header('Content-Type', 'text/csv');
        }

        return null;
    }

When trying to get a CSV response with:

Accept: text/csv
Content-Type: application/vnd.api+json

The controller hook is called and returns a HTTP response, but then the stack fails:

[2020-07-24 11:39:45] local.DEBUG: reading  
[2020-07-24 11:39:45] local.DEBUG: csv  
[2020-07-24 11:39:45] local.ERROR: Codec does not support encoding JSON API content. {"userId":2,"exception":"[object] (RuntimeException(code: 0): Codec does not support encoding JSON API content. at /home/project/vendor/cloudcreativity/laravel-json-api/src/Codec/Codec.php:101)
[stacktrace]
[stacktrace]
#0 /vendor/cloudcreativity/laravel-json-api/src/Http/Responses/Responses.php(495): CloudCreativity\\LaravelJsonApi\\Codec\\Codec->getEncoder()
#1 /vendor/neomerx/json-api/src/Http/Responses.php(91): CloudCreativity\\LaravelJsonApi\\Http\\Responses\\Responses->getEncoder()
#2 /vendor/cloudcreativity/laravel-json-api/src/Http/Responses/Responses.php(251): Neomerx\\JsonApi\\Http\\Responses->getContentResponse('col1, col2\
', 200, Array, NULL, Array)
#3 /vendor/cloudcreativity/laravel-json-api/src/Http/Responses/Responses.php(234): CloudCreativity\\LaravelJsonApi\\Http\\Responses\\Responses->getContentResponse('col1, col2\
', 200, Array, NULL, Array)
#4 /app/Http/Controllers/Api/V1/RegionsController.php(33): CloudCreativity\\LaravelJsonApi\\Http\\Responses\\Responses->content('col1, col2\
')
#5 /vendor/cloudcreativity/laravel-json-api/src/Utils/InvokesHooks.php(49): App\\Http\\Controllers\\Api\\V1\\RegionsController->reading(Object(App\\Models\\Region), Object(CloudCreativity\\LaravelJsonApi\\Http\\Requests\\FetchResource))
#6 /vendor/cloudcreativity/laravel-json-api/src/Http/Controllers/JsonApiController.php(406): CloudCreativity\\LaravelJsonApi\\Http\\Controllers\\JsonApiController->invoke('reading', Object(App\\Models\\Region), Object(CloudCreativity\\LaravelJsonApi\\Http\\Requests\\FetchResource))
#7 /vendor/cloudcreativity/laravel-json-api/src/Http/Controllers/JsonApiController.php(111): CloudCreativity\\LaravelJsonApi\\Http\\Controllers\\JsonApiController->doRead(Object(CloudCreativity\\LaravelJsonApi\\Store\\Store), Object(CloudCreativity\\LaravelJsonApi\\Http\\Requests\\FetchResource))
#8 [internal function]: CloudCreativity\\LaravelJsonApi\\Http\\Controllers\\JsonApiController->read(Object(CloudCreativity\\LaravelJsonApi\\Store\\Store), Object(CloudCreativity\\LaravelJsonApi\\Http\\Requests\\FetchResource), Object(App\\Models\\Region), 'regions')
#9 /vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): call_user_func_array(Array, Array)
#10 /vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php(45): Illuminate\\Routing\\Controller->callAction('read', Array)
#11 /vendor/laravel/framework/src/Illuminate/Routing/Route.php(219): Illuminate\\Routing\\ControllerDispatcher->dispatch(Object(Illuminate\\Routing\\Route), Object(App\\Http\\Controllers\\Api\\V1\\RegionsController), 'read')
#12 /vendor/laravel/framework/src/Illuminate/Routing/Route.php(176): Illuminate\\Routing\\Route->runController()
#13 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(680): Illuminate\\Routing\\Route->run()
#14 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Routing\\Router->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#15 /vendor/cloudcreativity/laravel-json-api/src/Http/Middleware/NegotiateContent.php(102): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#16 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(163): CloudCreativity\\LaravelJsonApi\\Http\\Middleware\\NegotiateContent->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#17 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#18 /vendor/cloudcreativity/laravel-json-api/src/Http/Middleware/BootJsonApi.php(78): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#19 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(163): CloudCreativity\\LaravelJsonApi\\Http\\Middleware\\BootJsonApi->handle(Object(Illuminate\\Http\\Request), Object(Closure), 'v1')
#20 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#21 /app/Http/Middleware/RequestLogger.php(19): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#22 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(163): App\\Http\\Middleware\\RequestLogger->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#23 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#24 /vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php(41): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#25 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(163): Illuminate\\Routing\\Middleware\\SubstituteBindings->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#26 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#27 /vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php(43): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#28 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(163): Illuminate\\Auth\\Middleware\\Authenticate->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#29 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#30 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#31 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(682): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#32 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(657): Illuminate\\Routing\\Router->runRouteWithinStack(Object(Illuminate\\Routing\\Route), Object(Illuminate\\Http\\Request))
#33 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(623): Illuminate\\Routing\\Router->runRoute(Object(Illuminate\\Http\\Request), Object(Illuminate\\Routing\\Route))
#34 /vendor/laravel/framework/src/Illuminate/Routing/Router.php(612): Illuminate\\Routing\\Router->dispatchToRoute(Object(Illuminate\\Http\\Request))
#35 /vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(176): Illuminate\\Routing\\Router->dispatch(Object(Illuminate\\Http\\Request))
#36 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(30): Illuminate\\Foundation\\Http\\Kernel->Illuminate\\Foundation\\Http\\{closure}(Object(Illuminate\\Http\\Request))
#37 /vendor/fideloper/proxy/src/TrustProxies.php(57): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#38 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(163): Fideloper\\Proxy\\TrustProxies->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#39 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#40 /vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php(21): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#41 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(163): Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#42 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#43 /vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php(27): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#44 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(163): Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#45 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#46 /vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php(62): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#47 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(163): Illuminate\\Foundation\\Http\\Middleware\\CheckForMaintenanceMode->handle(Object(Illuminate\\Http\\Request), Object(Closure))
#48 /vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php(53): Illuminate\\Pipeline\\Pipeline->Illuminate\\Pipeline\\{closure}(Object(Illuminate\\Http\\Request))
#49 /vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php(104): Illuminate\\Routing\\Pipeline->Illuminate\\Routing\\{closure}(Object(Illuminate\\Http\\Request))
#50 /vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(151): Illuminate\\Pipeline\\Pipeline->then(Object(Closure))
#51 /vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php(116): Illuminate\\Foundation\\Http\\Kernel->sendRequestThroughRouter(Object(Illuminate\\Http\\Request))
#52 /public/index.php(55): Illuminate\\Foundation\\Http\\Kernel->handle(Object(Illuminate\\Http\\Request))
#53 {main}

Package versions:

cloudcreativity/laravel-json-api: v1.7.0 neomerx/json-api: v1.09

What am I missing here?

lindyhopchris commented 4 years ago

Hi! The problem is you are using the $this->reply() helper to return non-JSON API content. The reply() helper only returns JSON API responses, which is why you're getting the error.

You should probably use Laravel's response() helper instead.

gaaf commented 4 years ago

Thanks a lot, this works. Been staring blindly at this for some time.