Content-Type in ErrorDocument - apache

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?
Set a Content-Type inside the .htaccess so the ErrorDocument doesn't have the text/html with a charset.
Set the error document to index.php in the directory the .htaccess is.
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.

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>

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); }
}

Related

Slim framework and GET/PUT/POST methods

For example, i use this code for testing routes:
$app->get('/api', function () {
echo 'get!';
});
$app->post('/api', function () {
echo 'post!';
});
$app->put('/api', function () {
echo 'put!';
});
For api testing i use RestClient plugin for Chrome.
When i try do GET request, response is 'get!'. Its good.
But:
When i try do POST request, response also is 'get!'. Why? Its must be 'post!'.
When i try do PUT request, (in Response Headers: Allow: GET,HEAD,POST,OPTIONS,TRACE ) Slim response have 405 error (Method Not Allowed) with message:
"The requested method PUT is not allowed for the URL /api."
What am I doing wrong?
Be sure that your .htaccess is the following (from slimphp/Slim#2.x):
RewriteEngine On
# Some hosts may require you to use the `RewriteBase` directive.
# If you need to use the `RewriteBase` directive, it should be the
# absolute physical path to the directory that contains this htaccess file.
#
# RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]

Convert apache htaccess rewrite rules to nginx rewrite rules

Could someone help me to convert this rules for nginx? I need it to serve html as xhtml for svg mask in FireFox
RewriteEngine on
RewriteBase /
RewriteCond %{HTTP_ACCEPT} application/xhtml\+xml
RewriteCond %{HTTP_ACCEPT} !application/xhtml\+xml\s*;\s*q=0
RewriteCond %{REQUEST_URI} \.html$
RewriteCond %{THE_REQUEST} HTTP/1\.1
RewriteRule .* - [T=application/xhtml+xml]
it is more complex
set $var "q";
if ($http_accept ~ application/xhtml\+xml) {
set $var "q$var";
}
if ($http_accept !~ 'application/xhtml\+xml\s*;\s*q=0') {
set $var "q$var";
}
if ($uri ~ \.html$) {
set $var "q$var";
}
if ($server_protocol = HTTP/1.1) {
set $var "q$var";
}
if ($var = qqqqq) {
add_header 'Content-Type' 'application/xhtml+xml';
}
not tested

Can I redirect to the newest file in directory using .htaccess?

I want to create .htaccess rule for situation like below:
I have a link to file: http://something.com/images/some/image_001.png
If this file doesn't exists I want to redirect to the newest file in /images/some directory
Is something like this possible using .htaccess? I know that I can check if file exists with RewriteCond, but don't know if it is possible to redirect to the newest file.
Rewriting to a CGI script is your only option from a .htaccess, technically you could use a programatic RewriteMap with a RewriteRule in a httpd.conf file.
The script can serve the file directly, so with an internal rewrite the logic can be entirely server side e.g.
.htaccess Rule
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-s
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^images/(.*)$ /getLatest.php [L]
Where getLatest.php is something like:
<?php
$dir = "/srv/www/images";
$pattern = '/\.(jpg|jpeg|png|gif)$/';
$newstamp = 0;
$newname = "";
if ($handle = opendir($dir)) {
while (false !== ($fname = readdir($handle))) {
// Eliminate current directory, parent directory
if (preg_match('/^\.{1,2}$/',$fname)) continue;
// Eliminate all but the permitted file types
if (! preg_match($pattern,$fname)) continue;
$timedat = filemtime("$dir/$fname");
if ($timedat > $newstamp) {
$newstamp = $timedat;
$newname = $fname;
}
}
}
closedir ($handle);
$filepath="$dir/$newname";
$etag = md5_file($filepath);
header("Content-type: image/jpeg");
header('Content-Length: ' . filesize($filepath));
header("Accept-Ranges: bytes");
header("Last-Modified: ".gmdate("D, d M Y H:i:s", $newstamp)." GMT");
header("Etag: $etag");
readfile($filepath);
?>
Note: Code partially borrowed from the answers in: PHP: Get the Latest File Addition in a Directory

rewrite rule for codeigniter

this is my controller in CI
class Welcome extends Controller {
function Welcome()
{
parent::Controller();
}
function index()
{
}
function bil($model='')
{ }
I want to do a rewrite so that
http://example.com/index.php/welcome/bil/model
becomes
http://example.com/model
in my htaccess I have
RewriteBase /
RewriteCond $1 !^(index\.php|images|robots\.txt)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /index.php/welcome/$1 [L]
#RewriteRule ^(.*)$ /index.php/welcome/bil/$1 [L]
I thought it should be as easy as removing the /index.php/welcome/ part
but when I uncomment the last line it get 500 internal server error
You'll want to use mod_rewrite to remove your index.php file like you have above, but use CodeIgniter's routing features to reroute example.com/model to example.com/welcome/bil/model.
In your routes.php configuration file, you can then define a new route like this:
// a URL with anything after example.com
// will get remapped to the "welcome" class and the "bil" function,
// passing the match as a variable
$route['(:any)'] = "welcome/bil/$1";
So then, typing example.com/abc123 would be equivalent to example.com/welcome/bil/abc123.
Note that only characters permitted by $config['permitted_uri_chars'] (which is located in your config.php file) are allowed in a URL.
Hope that helps!

How do I use HTTP Authentication for a specific URL (not a directory)

I have an htaccess file that uses mod_rewrite to redirect /controller to /index.php?controller=%controller%
Like this:
# Various rewrite rules.
<IfModule mod_rewrite.c>
RewriteEngine on
# Rewrite current-style URLs of the form 'index.php?controller=x&action=y'.
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?controller=$1 [L,QSA]
</IfModule>
Now, what I need to be able to do is make ONE of the controllers work with HTTP Authentication. I'm not asking if this is the best way to do things, I'm simply asking how to do it.
Example:
http://www.example.com/ - It requires no auth
http://www.example.com/secret - requires auth
<Location /secret>
AuthName localhost
AuthType Basic
AuthUserFile <file>
Require valid-user
</Location>
I ended up using PHP to do it:
if (in_array($controllerString, $configuration['protected']))
{
$authenticated = false;
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'You are unatuhorized to access this section of the website.';
} else if ($_SERVER['PHP_AUTH_USER'] == 'admin' && $_SERVER['PHP_AUTH_PW'] == 'admin'){
$authenticated = true;
}
if (!$authenticated)
{
unset($_SERVER['PHP_AUTH_USER']);
die();
}
}