Slim framework and GET/PUT/POST methods - api

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]

Related

Content-Type in ErrorDocument

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

Force SSL and handle browsers without SNI support

I was looking for a way to redirect webbrowsers with SNI support from http to https AND webbrowsers without SNI support from https to http. The second case is important, for example, if the user get a https url sent by mail and opens that with an old browser.
I wanted to avoid working with user-agents, because I didn't want to keep track of new browser versions and update the config files, e.g. .htaccess, accordingly.
Therefore, I use javascript to detect SNI support and redirect the webbrowser. The following code checks whether the connection is SSL secured. If not SSL secured, it checks for SNI support and redirects the browser if SNI is supported:
<head>
<script type="text/javascript">
if (window.location.protocol != "https:") {
var img=document.createElement('img');
img.src='https://www.example.org/favicon.png';
img.onload = function() {
window.location.href = "https:" + window.location.href.substring(window.location.protocol.length);
}
img.style.display='none';
document.body.appendChild(img);
}
</script>
...
</head>
If the user visits the website for the first time and makes use of SSL encryption, he gets redirected to the http url in order to run the javascript code. Otherwise an old browser opens the secured website and gets an error message displayed.
RewriteCond %{HTTPS} =on
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?example\.org [NC]
RewriteRule .* http://%{HTTP_HOST}%{REQUEST_URI} [R=302,L]
Update 1:
There is a redirect loop, because javascript doesn't deliver a referrer.
This solution seems to avoid the loop:
<body>
<form style="display:none;" method="get" name="redirect"></form>
<script type="text/javascript">
if (window.location.protocol != "https:") {
var img=document.createElement('img');
img.src='https://www.example.org/favicon.png';
img.onload = function() {
document.redirect.action = "https:" + window.location.href.substring(window.location.protocol.length);
document.forms['redirect'].submit();
}
img.style.display='none';
document.body.appendChild(img);
}
</script
...
</body>
Update 2:
We want to avoid redirecting the crawlers.
RewriteCond %{HTTPS} =on
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?example\.org [NC]
RewriteCond %{HTTP_USER_AGENT} !(googlebot|bingbot|baiduspider) [NC]
RewriteRule .* http://%{HTTP_HOST}%{REQUEST_URI} [R=302,L]
Update 3:
We use the more reliable https detection via PHP (no redirection loop).
<body>
<?php if (empty($_SERVER["HTTPS"])) { ?>
<form style="display:none;" method="get" name="redirect">
<input type="hidden" name="redirected" value="true" />
</form>
<script type="text/javascript">
var img=document.createElement('img');
img.src='https://www.example.org/favicon.png';
img.onload = function() {
var redirect_form = document.forms["redirect"];
redirect_form.action = "https:" + window.location.href.substring(window.location.protocol.length);
redirect_form.submit();
}
img.style.display='none';
document.body.appendChild(img);
</script>
<?php } ?>
...
</body>
Open issues:
How can I avoid the usage of user agents completely? If not possible, are there any important web crawler user agents missing?
With the approach described in update 3, the back button doesn't work correctly. If the user get forwarded from http to https and then clicks on the back button, he gets redirected to http and then from http to https again. This issue can be solved with location.replace(), but the method doesn't support referrer.
In the following example, how can I use location.replace() with http://www.example.org as referrer? Example: google.de -> http://www.example.org -> https://www.example.org -> back button click -> http://www.example.org (no redirection to google.de!!!) -> https://www.example.org
If the browser doesn't support javascript, it is forced to use http.
Are there any unknown problems with that approach?
Credits:
https://www.ebower.com/wiki/Detecting_SNI_with_Apache
https://coderwall.com/p/7a09ja/no-referer-after-redirect-solved
I decided to drop above htaccess rules due to the various problems. With the following code, web browsers that support Javascript and SNI get redirected to the SSL secured page without a check upon the user agent:
<body>
<?php if (empty($_SERVER["HTTPS"])) { ?>
<form style="display:none;" method="get" name="redirect"></form>
<script type="text/javascript">
var img=document.createElement('img');
img.src='https://www.example.org/favicon.png';
img.onload = function() {
var redirect_form = document.forms["redirect"];
redirect_form.action = "https:" + window.location.href.substring(window.location.protocol.length);
redirect_form.submit();
}
img.style.display='none';
document.body.appendChild(img);
</script>
<?php } ?>
...
</body>
These htaccess rules remove the trailing question mark created by above form:
RewriteCond %{HTTPS} =on
RewriteRule ^(.*)$ - [env=proto:https]
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ - [env=proto:http]
RewriteCond %{THE_REQUEST} \?\ HTTP [NC]
RewriteRule .? %{ENV:proto}://%{HTTP_HOST}%{REQUEST_URI}? [R=301,L]
Old web browser that don't support SNI are redirected to http if they access the page over https (What is the most efficient code to detect and redirect SNI supported browsers?):
SetEnv SSL_TLS_SNI %{SSL:SSL_TLS_SNI}
RewriteCond %{HTTPS} =on
RewriteCond %{HTTP_USER_AGENT} MSIE\s6 [NC,OR]
RewriteCond %{HTTP_USER_AGENT} Windows\sNT\s5 [NC,OR]
RewriteCond %{HTTP_USER_AGENT} Android.*(Mobile)?\ [0-3] [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^(.*.symbian.*) [NC,OR]
RewriteCond %{HTTP_USER_AGENT} ^(.*.blackberry.*) [NC]
RewriteCond %{SSL:SSL_TLS_SNI} =""
RewriteRule .* http://%{HTTP_HOST}%{REQUEST_URI} [R=307,L]

HTTPS and Zend Framework

I am trying to convert a functional ZF application to use SSL. The certificate is valid and works, but I am having trouble configuring the application.
Here's what's in .htaccess:
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
SetEnv APPLICATION_ENV development
IndexController is really simple:
class IndexController extends Zend_Controller_Action
{
public function indexAction() {
$auth = Zend_Auth::getInstance();
if ($auth->hasIdentity()) {
$this->_helper->redirector('index', 'dash');
} else {
$this->_helper->redirector('index', 'auth');
}
}
}
When I browse to the site without specifying https or port, it accurately routes me to https://app-url.com, but then tries to redirect to https://app-url.com/auth and returns a 403. What am I missing?
Assuming that 'index' is the name for the controller and 'dash' the name for the action, your parameters for the Redirector Helper1 are in the wrong order.
The first parameter is the action, the second the controller. So the correct method call would be
$this->_helper->redirector('dash', 'index');
This will redirect you to the URL /index/dash if no particular routes are set.
In the case that 'dash' is indeed the name of the controller and 'index' the name of the action, simply add a new controller named DashController which contains an indexAction() method and the redirect should work.
1) If you call the helper via $this->_helper->name this will call the direct() method, which in the redirector helper calls the method gotoSimple($action, $controller = null, $module = null, array $params = array())

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