Reputation: 693
I am building a small framework for my API's since they are quite specific, but I have a problem with the Content-Type when I received data for an ErrorDocument. Currently, I have the following .htaccess:
<IfModule mod_headers.c>
Header set Content-Type "text/plain"
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE"
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
RewriteRule ^([a-z]+)(/[A-Za-z0-9-\._\/]*)?$ $1.php [QSA,L]
ErrorDocument 404 "API_NOT_FOUND"
</IfModule>
What I want to achieve is the error 404 with a different Content-Type. Either text/plain or application/json would be fine, but none of those works. So probably I can't set the Content-Type header in the .htaccess like I want to. I also tried the ErrorDocument as a file, but since the path to the directory is dynamic, I can't use an error document without the path hardcoded like:
ErrorDocument 404 /api/index.php?error=404
The .htaccess is inside the api directory, but the directory can be renamed. Is there any way I can achieve one of the following things?
If the first one works, would I still be able to override it inside the .php scripts? Some of my calls are JSON, other are XML files.
Upvotes: 4
Views: 2995
Reputation: 693
Thanks for the answers and sorry to provide the final answer this late. I have found a solution which I think works like it should.
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP_AUTHORIZATION:%1]
RewriteRule ^([A-Za-z0-9_-]+)(/[A-Za-z0-9-\._\/]*)?$ $1.php [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteRule ^([a-z]+) index.php?error=404 [L]
</IfModule>
The error is redirected to the index.php which outputs the right stuff after doing the logging itself, so it is a win-win situation I believe. For the simple explanation, the following lines will be executed in the index.php:
http_response_code(404);
die(json_encode(['error' => ['code' => 'API_SCRIPT_NOT_FOUND', 'number' => 404]]);
Edit: I'll explain multiple things I do. The index.php normally generates a documentation, but when the index.php isn't called clean, I'll output the notfound error. It looks like this:
<?php
class Documentation {}
$API = new Documentation();
require_once('common/initialize.php');
Output::notfound('API_SCRIPT_NOT_FOUND');
The output class is a small class which handles the output with the correct Content-Type. It automatically set 'application/json' when no other Content-Type is set. A small example (there are more functions, but this is the one it runs):
class Output {
protected static $instance = null;
public static function instance() {
return self::$instance ?: self::$instance = new static;
}
private $finished = false;
private function finish($output, $status = null) {
if($this->finished) return; $this->finished = true;
http_response_code($status ?: 200); $content = null;
$headers = headers_list();
foreach($headers as $header) {
if(substr($header, 0, 13) == 'Content-Type:') {
$content = substr($header, 14); break;
}
}
if(!$content && !headers_sent()) {
header(sprintf('Content-Type: %s', $content = 'application/json'));
die(json_encode((http_response_code() >= 400) ? ['error' => $output] : $output));
}
die(!empty($output['code']) ? $output['code'] : $output);
}
public static function notfound($output) { self::instance()->finish(['code' => $output, 'number' => 404], 404); }
}
Upvotes: 0
Reputation: 786001
You can use ForceType
directive for this.
First create a file called error.json
inside your DocumentRoot/folder/
with this data:
{"error":"API_NOT_FOUND"}
Then in your DocumentRoot/folder/.htaccess
have it like this:
ErrorDocument 404 /folder/error.json
<Files "/folder/error.json">
ForceType application/json
</Files>
Upvotes: 3