I want to make user-friendly API for my service. I am trying to make it like Discord API - https://example.com/api/accounts/<account ID>/<stuff>/, some expected link examples below:
https://example.com/api/accounts/1234567890/data/
https://example.com/api/accounts/1234567890/inventory/
https://example.com/api/accounts/1234567890/servers/
I tried to make some htaccess tricks, but without any result - website got corrupted (just white page without anything) or account ID was considered as folder (HTTP 404, because I don't want those IDs as folders).
Thank you in advance for your response
Edit 1:
File structure can be really any, I just want it working. But most expected by me would be
api
- accounts
- data
- inventory
- ...
- server (example)
- game (example)
Edit 2:
My API is OOP - so I can call it from any place. I just need to get data and create new object and call some functions. In my bad-looking API, link was like: https://example.com/api/accounts/data.php?id=123456789 and in that script I call $stuff = new StuffObj($id, $other, $stuff, $from, $post); and function like $stuff->execute();. But with new API I can just make GET var, like action and in switch choose what object to use.
In my bad-looking API, link was like: https://example.com/api/accounts/data.php?id=123456789
In that case, to implement a "pretty" URL of the form /api/accounts/<id>/data/, you would do something like the following using mod_rewrite in your /api/.htaccess file:
Options -MultiViews
RewriteEngine On
RewriteRule ^accounts/(\d+)/data/$ accounts/data.php?id=$1 [L]
To make this more "generic", to rewrite a URL of the form /api/accounts/<id>/<action>/ to /api/accounts/<action>.php?id=<id>, assuming <action> would only consist of lowercase letters then you could change the above RewriteRule to read:
RewriteRule ^accounts/(\d+)/([a-z]+)/$ accounts/$2.php?id=$1 [L]
Although in your file structure you appear to have "subdirectories"(?) of the /accounts directory that also match the basename of the .php file? If so, this could be problematic, hence why I have disabled MutliViews above.
But with new API I can just make GET var, like action
Yes, you could - and would perhaps be preferable - everything is managed by a single entry point (eg. index.php - but you can call it anything your like) - However, you need to be specific and decide on this before implementing the "pretty URL" - which is really just cosmetic fluff. In fact, if this is just an API, do you need to implement a "pretty URL" at all?
Related
We currently have a full Angular project running in a sub-dir on our server and a physical "device" using a hardcoded URL to send a user to that page.
I'm looking for some kind of way to "intercept" the request via a PHP script first to (for example, not the real purpose) see if the requested "ID" param for that browser page has enabled the option to view the browser page or if it has been configured by the user to return a 406 HTTP response (for example).
Currently:
- ..com/app/routing-view?id=1234 => Angular view -> fetch info
Idea:
- ..com/app/routing-view?id=1234 => PHP-script -> isValid => forward to angular and do a normal 'webview' -> fetch info
- ..com/app/routing-view?id=2889 => PHP-script -> notValid => HTTP code
I thought about having a .htaccess "intercept" the url and forward it to a .php file. Do the magic and checks there, and then forward the browser to the original url, but to "bypass" the previous interceptor.
It's about that last part that I'm currently having issues. Because it's Angular and it is using paths, I can't just say "okay, redirect to index.html instead of index.php" because it would need to be something like ..com/app/routing-view?id=1234 (and the index.html is located in the /app directory.
I don't want to add PHP code to the original Angular-index file if that could be avoided.
Am I looking at this right or would there be a different, more efficient way to tackle this?
The reason for all this is that I want to (for example) return a different HTTP code or different headers to the device instead of a 200 html header response even if the ID turned out to be disabled or something like that.
Got it to work via a simple .htaccess, a parameter and an "interceptor" script. Quite simple and basic really, not sure why my brain didn't go this route before posting my question.
Added the .htaccess to the root of the Angular application with the following code.
RewriteEngine On
# Check if the request has been intercepted before (via URL parameter)
RewriteCond %{QUERY_STRING} !(^|&)intercepted=true
# .. If not, rewrite the page to use the interceptor script (only if it matches the route).
RewriteRule ^my-route dir-of-script/extra/interceptor.php?url=https://%{HTTP_HOST}%{REQUEST_URI}?%{QUERY_STRING} [L,QSA]
# Other stuff for rewriting the Angular routes etc
...
Added a /dir-of-script/extra/interceptor.php like-script that parses the URL from the GET parameter, fetches the info, does the checks and depending on the result, returns the output or redirects the page and let it through.
This adds a ..&intercepted=true parameter to the original URL so that the .htaccess doesn't intercept it again.
This seems to be working like a charm (for my situation). The only "downside" is that this counts as a redirect and not just a rewrite when it was allowed to go through. Will look further into it, to maybe let the PHP script "serve" the Angular content instead of redirecting to it.
Hi I'm currenly playing around with PHP MVC programming, and was wondering if anyone has made some sort of "routing" with a database?
I have a "page" table that looks like this: http://i.imgur.com/xS1OvjW.png
Currently all routes are hardcoded, and I thought it must be possible to do with a database..
But not only do I want to get the page and show it, I also need to be able to send parameters with it.
Example:
As shown in my page table, I have a "test" url. If i type http://demo.com/test/ I would get "rerouted" to use the "home" controller and "Index" method. But I also need to be able to type http://demo.com/test/id/40 and id/40 will be sent as params to the controller/method.
If this isn't a good thing to do, or if anyone got a better soloution please let me know! :)
Regards,
Frederik
This definitely depends on the server you're using, but since you're a PHP MVC noobie I'll reference apache in this example, and hope it's what you have for the sake of the examples.
First, you'll need your webserver configured to know that it has to send all traffic through your base page (usually index.php). Now that page would do some other stuff (call bootstrap, etc) but for the sake of argument we'll say that all it does right away is look at the request from the page, compare it to the DB, and complete the request if it can.
In that case, it will be helpful to have the request info from the server passed in to the index.php page. To do this, you'll want to configure apache with an .htaccess file similar to:
DirectoryIndex index.php
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.+)$ public/?path=$1 [L,QSA]
This tells it to use index.php for all requests that aren't specific files or directories, and to pass the full url path through to index.php as a $_GET var.
Next, in index.php, you'll want to check the path you were passed against DB of paths. Here's a really simple example to show:
<?php
// Obviously use your database and some string parsing here to match correctly
// I generally explode the path on '/' to break it into controller, action, etc.
if ($_GET['path'] == "user/account") {
// Then you call the controller that matches the first part of the route
// The action that matches the second part of your route
// And pass the request along so you can access anything else
call UserController::accountAction($_REQUEST);
} else if ($_GET['path'] == "user/resetpassword") {
call UserController::resetPasswordAction($_REQUEST);
}
From there, you should be in the right place and have everything you need. That Controller/Action URL format is a fairly common one for how easily it lets us do this.
Hope the answer helped!
I have a website that responds on *.domain.com.
Going to x.domain.com or y.domain.com should produce the same web page.
What * is I do not know, but it is and important piece of info since we track things based on it.
When moving to wordpress we ran into a pretty severe problem. It seems to generate links (using get_page_link) with the domain which is set in the admin.
This will not work for us because we can't find a way to tell wordpress to generate links without the domain (why does it do this anyway?!) and every time a link is clicked the browser goes from: x.domain.com to domain.com (since domain.com is what we have in the admin).
Unfortunately WordPress is architected such that it is really hard to get rid of the domain component of URLs. But all is not lost! Read on as the answer to your question requires a bit of background.
The WordPress team made the decision to require the user of a site to hardcode the site domain either in the database via an admin console, which you can see in the following screen shot, of via PHP which we'll discuss below:
You might ask what the difference is between the the two URLs? Even I find it confusing because I almost never need anything other them to have them both set to the root URL and as it's not important for your question I'll just gloss over that detail. If you are interested though you can learn more here:
Changing The Site URL
Editing wp-config.php: WordPress Address (URL)
Giving WordPress Its Own Directory
Moving along, the other option is to hardcode the two PHP constants WP_SITEURL and WP_HOME in the /wp-config.php file which can be found in the root of a WordPress installation. Those two lines might look like this in your /wp-config.php file:
define('WP_HOST','http://domain.com');
define('WP_SITEURL','http://domain.com');
The good news is that you can define both of them dynamically based on the current domain your site is serving from (I'm going to assume you have both your DNS server and your Apache web server configured for wildcard DNS.) You can use the following code to match any subdomain name consisting of letters and numbers:
$root_domain = 'domain.com'; // Be sure to set this to your 2nd level domain!!!
$this_domain = $_SERVER['SERVER_NAME'];
if (!preg_match("#^([a-zA-Z0-9]+\.)?{$root_domain}$#",$this_domain)) {
echo "ERROR: The domain [$this_domain] is not a valid domain for this website.";
die();
} else {
define('WP_HOME',"http://{$this_domain}");
define('WP_SITEURL',"http://{$this_domain}");
}
The bad news is you may have some "artifacts" to deal with after you get it working such as how URLs are handled for image URLs stored in the database content (which may or may not end up being a problem) or for Google Maps API keys, etc. If you have trouble with them let me suggest you post another question here or even better at the new WordPress Answers Exchange also run by the same people as StackOverflow.
As for telling WordPress how to generate links, there are filters you can "hook" but in my quick testing I don't think you need it because WordPress will generate the links for whatever domain happens to be your current domain. Still if you do find you need them you can do it although be prepared to be overwhelmed by all the add_filter() statements required! Each one controls one of the different ways links can be generated in WordPress.
Here is the hook filter function and the 40+ add_filter() calls; you might not need them all but if you do here they are:
function multi_subdomain_permalink($permalink){
$root_domain = 'domain.com';
$this_domain = $_SERVER['SERVER_NAME'];
if (preg_match("#^([a-zA-Z0-9]+)\.?{$root_domain}$#",$this_domain,$match)) {
$permalink = str_replace("http://{$match[1]}.",'http://',$permalink);
}
return $permalink;
}
add_filter('page_link','multi_subdomain_permalink');
add_filter('post_link','multi_subdomain_permalink');
add_filter('term_link','multi_subdomain_permalink');
add_filter('tag_link','multi_subdomain_permalink');
add_filter('category_link','multi_subdomain_permalink');
add_filter('post_type_link','multi_subdomain_permalink');
add_filter('attachment_link','multi_subdomain_permalink');
add_filter('year_link','multi_subdomain_permalink');
add_filter('month_link','multi_subdomain_permalink');
add_filter('day_link','multi_subdomain_permalink');
add_filter('search_link','multi_subdomain_permalink');
add_filter('feed_link','multi_subdomain_permalink');
add_filter('post_comments_feed_link','multi_subdomain_permalink');
add_filter('author_feed_link','multi_subdomain_permalink');
add_filter('category_feed_link','multi_subdomain_permalink');
add_filter('taxonomy_feed_link','multi_subdomain_permalink');
add_filter('search_feed_link','multi_subdomain_permalink');
add_filter('get_edit_tag_link','multi_subdomain_permalink');
add_filter('get_edit_post_link','multi_subdomain_permalink');
add_filter('get_delete_post_link','multi_subdomain_permalink');
add_filter('get_edit_comment_link','multi_subdomain_permalink');
add_filter('get_edit_bookmark_link','multi_subdomain_permalink');
add_filter('index_rel_link','multi_subdomain_permalink');
add_filter('parent_post_rel_link','multi_subdomain_permalink');
add_filter('previous_post_rel_link','multi_subdomain_permalink');
add_filter('next_post_rel_link','multi_subdomain_permalink');
add_filter('start_post_rel_link','multi_subdomain_permalink');
add_filter('end_post_rel_link','multi_subdomain_permalink');
add_filter('previous_post_link','multi_subdomain_permalink');
add_filter('next_post_link','multi_subdomain_permalink');
add_filter('get_pagenum_link','multi_subdomain_permalink');
add_filter('get_comments_pagenum_link','multi_subdomain_permalink');
add_filter('shortcut_link','multi_subdomain_permalink');
add_filter('get_shortlink','multi_subdomain_permalink');
add_filter('home_url','multi_subdomain_permalink');
add_filter('site_url','multi_subdomain_permalink');
add_filter('admin_url','multi_subdomain_permalink');
add_filter('includes_url','multi_subdomain_permalink');
add_filter('content_url','multi_subdomain_permalink');
add_filter('plugins_url','multi_subdomain_permalink');
add_filter('network_site_url','multi_subdomain_permalink');
add_filter('network_home_url','multi_subdomain_permalink');
add_filter('network_admin_url','multi_subdomain_permalink');
While brings us to the final point. There is functionality in WordPress that attempts to ensure every URL that is loaded is served via its canonical URL which in general is a web best practice, especially if you are concerned with optimizing search engine results on Google and other search engines. In your case, however, if you really do not want WordPress to redirect to your canonical URL then you need to add a redirect_canonical filter hook and tell WordPress not to do it.
What follows is the code to make sure any page that serves as "x.domain.com" stays on "x.domain.com" even if all the URLs are filtered to be "domain.com". That may not be the exact logic you need but I'm just showing you the building blocks of WordPress so you'll be able to figure out the logic that you require.
A few final details about this function call; parameters #3 and #4 refer respectively to the priority (10 is standard priority so this hook will not be handled special) and the number of function arguments (the 2 arguments are $redirect_url and $requested_url.) The other thing to note is that returning false instead of a valid URL cancels the canonical redirect:
add_filter('redirect_canonical','multi_subdomain_redirect_canonical',10,2);
function multi_subdomain_redirect_canonical($redirect_url,$requested_url){
$redirect = parse_url($redirect_url);
$requested = parse_url($requested_url);
// If the path+query is the same for both URLs, Requested and Redirect, and
if ($redirect['path']+$redirect['query']==$requested['path']+$requested['query']) {
// If Requested URL is a subdomain of the Redirect URL
if (preg_match("#^([a-zA-Z0-9]+).{$redirect['host']}$#",$requested['host'])) {
$redirect_url = false; // Then cancel the redirect
}
}
return $redirect_url;
}
That's about it. Hope this helps.
-Mike
do you have any control over your hosting? Maybe you can use the rewrite module in apache, if you are using apache.
In httpd.conf add:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^x\.domain\.com
RewriteRule ^(.*)$ http://www.domain.com/x/$1
RewriteCond %{HTTP_HOST} ^y\.domain\.com
RewriteRule ^(.*)$ http://www.domain.com/y/$1
You can change the way you pass the variable "v" too
RewriteRule ^(.*)$ http://www.domain.com/$1&var=v
I didn't try the code, but i'm pretty sure that at least it will open your mind to a new way to deal with this issue --one that doesn't involve so much coding.
Cheers.
A.
I'm trying to alter the way we currently provide a gateway page system within our CMS. What i mean by gateway page is mapping a non-existent URL to a page through a rewrite rule in the .htaccess, e.g.
RewriteRule ^foobar$ page.php?mode=bar&method=foo&id=1
This allows people to create short links to CMS pages for magazine adverts etc. The problem with this method is that it relies on access to the .htaccess. I would prefer a method that sits at code level but it occurs to me that - without a rewrite rule - a 404 error will be called. Is there any way to prevent this or work around this?
You can use a rewritemap within your htaccess. What this does is references an external file/script, passing it the incoming uri and getting back the rewritten uri.
In your .htaccess
RewriteMap shorts prg:/path/to/map.php
In your php file map.php
#!/path/to/php
$keyboard = fopen("php://stdin","r"); // get data from stdin
while (1) {
$line = trim(fgets($keyboard));
// fetch rewrite for line and echo out
}
The php file is passed the short url and returns - based on your logic - the full url
this is the URL path I currently use:
/index.php?page=1&title=articles
I want to get the URL path as
/index/page-1/title-articles
using SEO user friendly URLs in PHP.
And how to get the value of the "title"? Any one can help me pls.
Check out the mod_rewrite module, and maybe for a good starting point, this tutorial on how to take advantage of it with PHP.
You need to ensure two things:
your application prints out the new URLs properly, and
your webserver can understand that new URLs and rewrites them to your internal scheme or redirects them back to your application and your application does the rest.
The first part can be simply accomplished by using
echo ' … ';
instead of
echo ' … ';
The second part can be accomplished either with URl mapping features of your webserver (most webservers have a module like Apache’s mod_rewrite). With mod_rewrite, the following will do the rewrite:
RewriteEngine on
RewriteRule ^index/([^/-]+)-([^/]+)(.*) /index$3?$1=$2 [N,QSA]
RewriteRule ^index$ index.php [L]
The first rule will extract one parameter at a time and append it to the query. The second rule will finally rewrite the remaining /index URL path to /index.php.
I want to get the URL path as
/index/page-1/title-articles
Why? I’ve got two objections:
index is a no-information and I doubt that it belongs in the URI
page-1, as well as title-articles looks plain weird. Like with index, you should ask yourself whether this information belongs here. If it does, make clear that it’s the key of a key-value pair. Or remove it entirely.
Thus, I propose either:
/‹article›/1
or
/‹article›/page/1
or
/‹article›/page=1
or
/‹article›[1]
or
/articles/‹article›/page/1
Or any combination thereof. (In the above, ‹article› is a placeholder for the real title, the other words are literals).