Viktor
Viktor

Reputation: 507

Apache rewrite rules for Slim (PHP) RESTful API

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?

Upvotes: 3

Views: 8315

Answers (3)

smcjones
smcjones

Reputation: 5600

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.

Upvotes: 2

Croises
Croises

Reputation: 18671

Use this in your .htaccess #2 (in api/) :

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ public/index.php [L]

Upvotes: 0

guillermoandrae
guillermoandrae

Reputation: 610

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]

Upvotes: 0

Related Questions