I'm having some trouble configuring my RESTful API (using Slim PHP and Eloquent ORM) to rewrite URL's correctly.
My project directory structure goes like this:
wwwroot/
myproject/
api/ (maintained in its own Git repo)
app/
data/
lib/
models/
routes/
app.php (where Slim is instantiated and used)
config.php (holds database settings, etc.)
public/
.htaccess (#1)
index.php (includes ../app/app.php)
vendor/
.htaccess (#2, this be empty yo)
ngapp/ (future Angular.js app)
webapp/ (future standard web app)
.htaccess (#1) contains the standard rules:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
This makes the API reachable via http://localhost/myproject/api/public/<insertAnySlimRouteHere>. However, I want to reach the API via an URL such as this: http://localhost/myproject/api/<insertAnySlimRouteHere>. How do I do this?
(Feel free to comment on my overall project structure too. I'm trying to learn how to develop a RESTful API in a language I seldom use nowadays since I'm employed as a .NET/JS developer, using two frameworks I've never dabbled with before and making it (the API) suitable for several clients, both on web and on mobile devices. Learning is fun, whenever there is time.)
Update
I followed smcjones' suggestion and added the following to my httpd-vhosts.conf:
<VirtualHost *:80>
Alias "/lampjavel/api" "C:/Users/Viktor/Dev/htdocs/lampjavel-api/public"
<Directory "C:/Users/Viktor/Dev/htdocs/lampjavel-api">
Options Indexes FollowSymLinks MultiViews
AllowOverride all
Order allow,deny
Allow from all
</Directory>
</VirtualHost>
I also deleted my .htaccess #2 and my .htaccess #1 (in public/) looks like this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.php [QSA,L]
However, the first part of the Slim route gets thrown away. When I access .../api in browser I get the / route, which is correct. But when I request .../api/images I still get the / route. If I delve deeper to .../api/images/images I get my desired route, /images.
In my app.php:
$app->get('/', function () use ($app) {
echo '/';
});
$app->get('/images', function () use ($app) {
echo '/images';
});
Any idea why the first part of the route gets ignored?
What you are looking to accomplish in its stated form would be unwieldy and insecure.
Unwieldy, because you are trying to call child folders from the parent. This could lead to several unforeseen issues. For example:
You need to disallow the folders which are in your project structure from being called using your mod_rewrite. Otherwise, your server will try to serve you a Slim route instead of, say, app. While you could add RewriteCond %{REQUEST_FILENAME} !-d, that would miss the point, because then your app route wouldn't work on Slim.
For this reason you will also never be able to have a Slim route named app. While you most likely have no intention of having a Slim route named app, it is better to give your application the flexibility.
On a similar note, if you ever decided you needed another folder (let's say, docs, or test for unit testing), you would either need to bury it under another folder, like app or public (where, at least to me, they don't seem like they fit), or you will need to change your mod_rewrite. You will also need to look out for naming conflicts all over again. In this sense, unless you are 100% sure you are not going to modify your structure, you're adding more pain for yourself than would be necessary.
Insecure, because your web path is currently set to show a big portion of your directory structure. While this seems to me like a development server, where it may not matter, I think it's good practice to get your paths in order on your development server first.
Currently, if you navigate to http://localhost/myproject/api/app/data, does it throw a 404? Would someone be able to guess your structure? Are there any pages that you would not want someone to find outside of your root? While PHP will not present any of these files, someone would easily be able to overload your system by calling files. Also, if a file throws an error when called alone, you may be giving up more information about your path.
Again, I realize that you are probably just testing this out, but if it is going into a live environment at some point, these are issues you will need to deal with. I have given myself a lot less grief after I started trying to make my development server emulate my live server as much as possible.
My Proposed Solution
.htaccess is really not the ideal place to conduct these sorts of operations. Instead, you should be looking at Apache's configuration files. As #guillermoandrae stated, what it sounds like you're really looking to do is have a different DocumentRoot.
If you can access Apache's configuration you can create a VirtualHost which points directly to the public directory.
Example:
<VirtualHost *:80>
Alias "/api" /wwwroot/myproject/api/public
<Directory "/api">
Options Indexes FollowSymLinks MultiViews
AllowOverride all
Order allow,deny
Allow from all
<IfModule mod_rewrite.c>
RewriteEngine on
#Rewrite Rules from .htaccess here
</IfModule>
</Directory>
</VirtualHost>
You can add more Directory tags here and create a secure application with aliases to folders you need to reach.
A (long) note about your project structure
I already mentioned that your project structure may have to change at some point. Overall I would mention that your project structure looks good. PHP is an overly forgiving language, which can be bad, but with your background means you'll be doing just fine. However, one thing I would suggest is that you split off your api folder into its own project. The reason I recommend this is because it is currently pretty tightly woven with your one application. You mentioned you want it to work with multiple applications. Well, the beauty of an API is that it works so long as you can access the URI. So long as you are creating a RESTful API (or even a JSon-based API), any language can parse your API regardless of where it is.
Currently, if you created a new project, say mysecondproject, you would have two identical URL's:
http://localhost/myproject/api/public/foo and http://localhost/mysecondproject/api/public/foo would both go to the same place. Unless you did not update the project in one place, in which case you would find inconsistencies.
Ultimately I would suspect that unless you are looking to have mirrors, you probably want your API to exist in one location on the internet. This gives you better control over, well, everything.
EDIT
Based on your questions below, your directory is being rerouted due to your mod_rewrite code. Without knowing what you're using right now for rewrites, I will suggest you add this single line and see how it works (after ...%{REQUEST_FILENAME} !-f)
RewriteCond %{REQUEST_FILENAME} !-d
EDIT #2
Your htaccess code is currently pushing everything that is not a directory or a file off of your main directory to Slim. So chances are good the error is in your Slim routes and not your Apache access. Do you have a route that looks something like this?
$app->get('/images/:route', function ($route) {
//execute code
};
If you do not, then try to figure out what route is being implemented. You can probably use some of Slim's built in logging capabilities if it is hard to locate.
It sounds like the DocumentRoot for your app is actually wwwroot/myproject/api and you want it to be wwwroot/myproject/api/public. You could just set the DocumentRoot directive, or just do this:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ /public/index.php [QSA,L]
Use this in your .htaccess #2 (in api/) :
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ public/index.php [L]
Related
I've been researching and trying for a week to accomplish this, but I haven't been able to find my solution. I'm sure it's possible, but I'm just not an expert in the depths of voodoo magic.
The setup:
An installation of MantisBT located in
mysite.com/mantisbt/currentver/mantis-1.3.19
When I perform an upgrade, I want to archive all old versions and old
database dumps to /mantisbt/oldversions/ to keep things tidy.
I also have other utilities and pages in various subdirectories, for
instance "mysite.com/utils"
The goal:
I want users to type in mysite.com/ (root) and have the URL rewritten
(transparently) to /mantisbt/currentver/mantis-1.3.19/ so that they
don't see my directory structure and it looks like the mantisbt app is
located in the root directory.
I also want protection from anyone trying to directly access a
subdirectory beginning with "/mantis". For instance, if
someone directly types mysite.com/mantisbt/currentver/mantis-1.3.19/
into their browser, I want them redirected back to the root directory
so they access the site from root just like everyone else.
I also need to allow my other subdirectories like mysite.com/utils to
be accessible if I type in the full path to them.
The biggest problem I've encountered is that Apache loops through your .htaccess file again every time the URL changes. So I get stuck in these rewrite loops. I've tried looking at every possible tool that Apache offers, but I'm seriously outgunned here. I could provide examples of what I've tried, they're obviously not correct, but ask me and I can post them.
You'd be far better off altering the DocumentRoot in your httpd.conf, and mapping in the utils directory, via a Location directive. It will be far cleaner, and Apache won't have to do some trickery with every request.
Anyway something along the following lines should work.
# Stop direct access
RewriteCond %{HTTP_REFERER} !mysite.com [NC]
RewriteRule /?mantisbt/currentver/mantis-1.3.19 / [NC,L,R=301]
# Internally Rewrite everything apart from /utils to /mantisbt/mantis-1.3.19/
RewriteCond %{HTTP_REFERER} !mysite.com [NC]
RewriteCond %{REQUEST_URI} !/utils [NC]
RewriteRule .* /mantisbt/currentver/mantis-1.3.19%{REQUEST_URI} [L]
Im running an MVC based application on my mainsite, I have 2 other domains (for the sake of an example, www.a.com & www.b.com)
I'd like to be able to handle all a.com's requests with mainsite.com/a/ and similarly b.com with mainsite.com/b/
However I do not want the url to be redirected/changed in the browser.
I've been trying with mod_rewrite, however it seems to be clashing with my existing .htaccess rules set for mainsite.com
this is my existing .htaccess
Could anyone please suggest the best way to do this?
In the existing .htaccess, I don't see any rules redirecting the domains a.com or b.com. To do that is pretty straightforward, though.
A condition for selecting the proper host www.a.com or a.com
RewriteCond %{HTTP_HOST} ^(?:www\.)?a\.com$
prevent an endless loop
RewriteCond %{REQUEST_URI} !^/a/
and do the actual rewrite
RewriteRule ^ /a%{REQUEST_URI} [L]
As long as you don't use the R flag, the URL shouldn't change in the browser.
The rule for host b.com is analogous.
Update:
Since you already have a very large .htaccess file, the performance impact shouldn't matter too much. If you want to know for sure, there's no substitute for measuring.
If you want to reduce the performance hit nevertheless, you have two options
Move the directives in the .htaccess file to your main config or virtual config file, see When (not) to use .htaccess files for an explanation.
Do some custom rewriting with PHP in your front controller. This depends on the framework or routing mechanism you use, of course.
I'm redoing my API. Fortunately, its versioned. Yay! But what's the best way to handle the new code.
Basically; I have api.example.com/v1/method etc. My site runs on apache, and api.example.com points to /var/www/html/api.example.com
For the new API, I will be using a completely different framework and codebase etc, so I don't want to mix up the files.
Ideally, what I want is api.example.com/v1/ to point to /var/www/html/api-v1.example.com and api.example.com/v2/ to point to /var/www/html/api-v2.example.com.
[Edited, I originally said subdirectories which I think is confusing, but really I think I want them as completely separate sites]
Is this possible using Apache?
Additional question: if instead of using /v1/ /v2/ etc, I versioned my API by using an Accept header like:
Accept: application/vnd.api-example-com-v2+json
how would I map that to a different directory?
It's fine if you have your document root setup as
/var/www/html/api.example.com
Then have the directories /v1/ and /v2/ inside the document root. As for the Accept header. You can match against that using mod_rewrite:
RewriteEngine On
RewriteCond %{HTTP_ACCEPT} application/vnd.api-example-com-(.*)\+json
RewriteCond %1::$1 !^(.*)::\1
RewriteRule ^(.*)$ /%1/$1 [L]
If that's what you want, then you need to create some aliases in your vhost/server config:
AliasMatch ^/v([0-9]+)/(.*)$ /var/www/html/api-v$1.example.com/$2
I'm working with an online encyclopedia and I am trying to achieve the following:
Given the physical location of a file in http://example.com/articles/c/a/t/Cat.html,
Get the location in the address bar to show http://example.com/encyclopedia/Cat.html
This also needs to work so that if a link is clicked or someone types in "example.com/encyclopedia/Cat.html", the server will look for the file in "/articles/c/a/t/Cat.html", yet still serve the shorter URI in the address bar.
I understand this may involve some heavy .htaccess voodoo to accomplish, or perhaps that it would be better to use a PHP script to serve this purpose.
So far I have the following in my .htaccess:
<IfModule mod_rewrite.c>
Options +FollowSymLinks
RewriteEngine On
RewriteRule ^encyclopedia/(.*)\.html$ articles/$1.html [NC]
RewriteCond %{THE_REQUEST} ^GET\ articles/(.*)
RewriteRule ^articles/(.*) /encyclopedia/$1 [L,R=301]
</IfModule>
However with this code, it only works by going to "example.com/encyclopedia/c/a/t/Cat.html" and showing the proper page, and when you go to "/articles/c/a/t/Cat.html it still doesn't rewrite it as "/encyclopedia/", it just stays the same.
Edit - By removing the GET\ part from the RewriteCond and removing the leading forward-slash from /encyclopedia/$1 in the following line, any requests to "/articles/c/a/t/Cat.html" are correctly redirected to "/encyclopedia/c/a/t/Cat.html". I am still at a loss trying to remove the "/c/a/t" part though. **
I've tried using the following two rules to remove the "c/a/t/" part:
RewriteRule ^encyclopedia/((.)(.)(.).*)\.html$ articles/$2/$3/$4/$1.html [NC]
RewriteRule ^articles/(.)/(.)/(.)/(.*) /encyclopedia/$4 [L,R=301]
But with no success as I'm sure what's happening is I'm getting the capital "C" from "Cat.html" and putting that in as "/articles/C/a/t/Cat.html" which will obviously not work.
I've been looking around studying .htaccess RewriteRule and RewriteCond for days but I still haven't been able to figure this out and been BHOK enough to cause a few migraines.
Would this be better accomplished using a PHP script? Or can this voodoo be easily enough accomplished via only .htaccess rules?
First thing, forget about .htaccess files. .htaccess files is just an extension of Apache configuration files that you can put in some directories. They're really slowing down your apache server, he needs to check part of his configuration at runtime. It's done to allow some configuration on hosted environments.
Put everything you have in .htaccess files in <Directory> sections on your VirtualHost and use AllowOverride None to tell Apache to forget about trying to read .htaccess files.
So what you need is mod-rewrite voodoo, not .htaccess voodoo :-)
Now your rewrite problem is quite complex. If you need some mod-rewrite help do not forget to read this ServFault article : Everything You Ever Wanted to Know about Mod_Rewrite Rules but Were Afraid to Ask?
I assume that your Cat.html -> c/a/t/Cat.html is just an example and that you can have more than 3 letters : CatAndDogs.html -> c/a/t/a/n/d/d/o/g/s/CatAndDogs.html.
The part of mod-reqrite you need is (I think) RewriteMap. There you will find some helpers like lowercase: that coudl help you, but you will also find the prg: which means using an external program to perform the mapping. I would use perl examples of such rewriteMaps examples available via google and make some transformations. Should be quite easy and Fast in Perl to transform CatAndDogs.html in c/a/t/a/n/d/d/o/g/s/CatAndDogs.html.
Note that RewriteMap will never work inside a .htaccess. Forget .htaccess files. The prg: keyword will launch your perl program as a parallel daemon and will feed him with quite a lot of data, you shoudl really write something robust & fast. Do not forget to use the RewriteLock directive to avoid mixing results (some prg: mappers do not care about mixing results, think about load balancers for examples, but you do want to avoid mixing results for parallel queries)
My sites root access is managed by htaccess: it redirects various aliases to their own home files /en/home for english /de/home for Deutsch etcettera. Previously, I used index.php to route and redirect all that, and hence the DirectoryIndex had something like this:
DirectoryIndex /index.php
Now, however, there is no index.php file, so I commented it
# DirectoryIndex /index.php
Would it be better to uncomment is and set it to the default /en/home (with or without .php because in this case? I have set up rules sohat my pages in browser also work when no extension is given)
DirectoryIndex /en/home
In all the above cases, my websites work fine and I don't see ANY change when I set either of the three instances as above. but ... "there's gotta be one best ain't it?"
Thanks!
If you have the rules written in .htaccess it is best not to repeat the rules in whatever php config and routing functions you are using. Routing through apache (your .htaccess) is much faster than subverting routes through php, though you will not realize the gains without a pretty high volume of traffic.