Closed ben221199 closed 3 years ago
I'm using a website and an API in one single Laravel project.
In the RouteServiceProvider
I have this code:
/**
* Define the routes for the application.
* @return void
*/
public function map(): void{
$this->mapApiRoutes();
$this->mapWebRoutes();
}
/**
* Define the "api" routes for the application.
* These routes are typically stateless.
* @return void
*/
protected function mapApiRoutes(): void{
Route::middleware('api')->domain('api.mydomain.test')->name('api:')->group(base_path('routes/api.php'));
}
/**
* Define the "web" routes for the application.
* These routes all receive session state, CSRF protection, etc.
* @return void
*/
protected function mapWebRoutes(): void{
Route::middleware('web')->group(base_path('routes/web.php'));
}
Can you confirm what the Accept
header is when you send the request to the endpoint that doesn't exist?
If your Accept
header has the value application/vnd.api+json
, you should get a JSON:API response. If it doesn't, then the HTML response is correct.
But why should I set an Accept
header when the whole domain (api.mydomain.test
) is meant to give API responses?
Because that's how HTML content negotiation works. From Mozilla:
The Accept request HTTP header advertises which content types, expressed as MIME types, the client is able to understand. Using content negotiation, the server then selects one of the proposals, uses it and informs the client of its choice with the Content-Type response header.
Technically a Laravel app out-of-the-box supports multiple content types from the exception handler: HTML and JSON. The JSON:API package effectively adds JSON:API in as a supported type - but doesn't override the fact that the application renders exceptions for HTML and regular JSON. I.e. we add an additional supported Content-Type that the exception handler can choose to return.
If you want your exception handler to only over render as JSON:API then you'd need to just set up your render method on the exception handler so it only ever returns JSON:API. (Or only ever returns it if the request URI matches your whole API domain.) I.e. implement your own logic for isJsonApi()
.
Does that make sense?
Hmmm, I could try to look for a specific namespace or guard.
You could probably check whether it's the api
domain using a method on the request. There's a fullUrlIs()
method that looks useful for matching the domain in the URL.
Going to close this, as I think the issue is answered - we add an additional media type supported by the exception handler, not replace them.
If a developer wants to only ever render JSON:API, then this:
public function render($request, Throwable $e)
{
if ($this->isJsonApi($request, $e)) {
return $this->renderJsonApi($request, $e);
}
// do standard exception rendering here...
}
Should be replaced with this:
public function render($request, Throwable $e)
{
return $this->renderJsonApi($request, $e);
}
Or if wanting to check if it's their API they can use the request is()
or fullUrlIs()
methods, e.g.:
public function render($request, Throwable $e)
{
if ($request->is('/api*')) {
return $this->renderJsonApi($request, $e);
}
// do standard exception rendering here...
}
In the Exception handler there is the following function:
In this function, the code
$this->isJsonApi($request,$e)
detects if the current endpoint is a JSONAPI endpoint. If it is, it will render the exception in JSONAPI format.However, when the endpoint does not exist, this
$this->isJsonApi($request,$e)
returns false and Laravel will show the non-JSONAPI version of a 404 page, so the page will return HTML. What do I have to do to get a JSONAPI error when the endpoint doesn't exist? I don't want to useRoute::fallback
.Example:
/accounts/v1/users
: This endpoint exists, all exceptions here (like auth-exceptions) will be in JSONAPI format./accounts/v1/us_ers
: This endpoint does not exist because of a typo. However, at the moment it will give a 404 HTML page.