用户友好的 URL - mod_rewrite 和 php 重定向

Posted

技术标签:

【中文标题】用户友好的 URL - mod_rewrite 和 php 重定向【英文标题】:User friendly URLs - mod rewrite and php redirections 【发布时间】:2012-02-24 17:31:42 【问题描述】:

到目前为止,我已经这样做了:

RewriteBase /
RewriteCond  %REQUEST_FILENAME !-f
RewriteCond  %REQUEST_FILENAME !-d
RewriteRule  ^(.*)$ index.php?load=$1 [QSA,L]

然后在我的索引页面(在根目录中)我使用 PHP 来确定要加载哪个页面:

// Swap to variables
    $load = $_GET['load'];

// Home page
    if (!$load || $load == "")  include('home.php'); exit(); 

// Dashboard
    if ($load == "dashboard")  include('dashboard.php'); exit(); 

// About
    if ($load == "about")  include('about.php'); exit(); 

// Username
    $data_array = array(':username' => $load);
    $select_account = $connect->prepare("SELECT * FROM `users` WHERE `username` = :username");
    $select_account-> execute($data_array);
    $account_amount = $select_account->rowCount();
    if ($account_amount > 0)  include('profile.php?name=$load'); exit(); 

// Redirect to 404 if there are no results
    include('404.php'); exit();

到目前为止一切正常,但用户可以将照片上传到画廊,我希望它们被这样查看:

www.mysite.com/[username]/gallery/

但是,如果您将其输入为 url,则重写会将 [username]/gallery/ 作为一个部分,这意味着 $load = [username]/gallery 会给我一个“404”。

可能有更好的解决方案来获得所需的结果,但我不太擅长 .htaccess 和重写。我想补充一点,我也喜欢这种重写,因为我有名为 signupsignin 的子目录,它们也都有子目录,但是如果我转到 URL:

www.mysite.com/signup
www.mysite.com/signin

它忽略重写并转到目录,而不是通过我想要的 $load 语句运行它。

另外,需要注意的是,在注册帐户时,任何与 dashboardabout 等字符串匹配的用户名都不允许他们使用它,这会停止用户名和 $load if/else 语句他们包括被混淆等

编辑

我忘记注意的另一件事是,由于他们可以随意调用画廊,因此需要进行搜索以查看该画廊是否存在,例如:

www.mysite.com/username/my+first+album

首先需要检查用户名是否存在,然后检查相册是否存在,如果存在则显示它,如果不存在则 404/重定向到任何地方。所以基本上,两个参数/查询都是动态的。不仅如此,该相册中的单张照片也需要同样工作,例如:

www.mysite.com/username/my+first+album/my+picture

我希望这是有道理的......

【问题讨论】:

【参考方案1】:

您想要的是所谓的 URL 路由器,这需要您分析 url 并根据内容做出决策。大多数系统通过让您提供一个 url 模板和一个在 url 匹配时调用的函数来做到这一点。该函数通常会传递模板 url 中的任何子匹配项。

例如,Django 使用正则表达式进行 url 路由,并将命名匹配作为参数传递给给定函数(或类)。

如果这对您的需求来说太复杂了,那么您可以使用特定的正则表达式来解析 url,您的画廊案例将是:

$matches = array();
$re = "/\/([\w\d])+\/([\w\d+%])+\/?/";
preg_match($re, $load, $matches);
$username = $matches[0];
$gallery  = $matches[1];

然后您可以随意使用$username$gallery

注意

以上假设它会匹配,你需要检查preg_match的返回值来确定。另外,我没有检查正则表达式,它可能是错误的,或者使用了不在此语法中的功能。

参考

Regular Expressions PHP PCRE Function Documentation(PCRE = Perl 兼容的正则表达式)

【讨论】:

我在这个 URL 路由上查了一下,以前从未听说过,但我想出了一个很好的方法来使用它的一些部分以及来自你们的答案的一些想法和莎莉给了我。我将把它写在一个答案中,以展示我是如何运行的。 +1 为您提供帮助!【参考方案2】:

一个简单的解决方案是: 编辑 HTACCESS

RewriteBase /
RewriteCond %REQUEST_URI  !/signup
RewriteCond %REQUEST_URI  !/signin
RewriteRule ^([^/]*)/([^/]*)$ index.php?load=gallery&username=$1&gallery=$2
RewriteCond  %REQUEST_FILENAME !-f
RewriteCond  %REQUEST_FILENAME !-d
RewriteRule  ^(.*)$ index.php?load=$1 [QSA,L]

现在 PHP 部分(用于 index.php):

$load = $_GET['load'];
switch ($load)
    default:
        include('home.php');
        exit();
    break;
    case 'dashboard':
        include('dashboard.php');
        exit();
    break;
    case 'about':
        include('about.php');
        exit();
    break;
    case 'gallery':
        $username = $_GET['username'];
        $gallery = $_GET['gallery'];

        //check for the username and gallery

        header("Location: your-gallery-location-goes-here");
    break;

希望它会有所帮助:)

【讨论】:

正如我刚刚在 Aatch 的回答中所说,我已经使用了你自己和 Aatch 的帮助以及 Aatch 提到的一些关于 URL 路由的文章。我现在正在写一个答案来展示我所拥有的。感谢您的帮助 +1【参考方案3】:

在 Aatch 和 Sally 的帮助以及一些关于 URL 路由的搜索结果的帮助下,我有以下方法来实现我所追求的,所以我想我会与大家分享它,以防有人想使用它...

首先我需要提到我正在处理的站点位于根文件夹 mysite.com/sub/folder/index.php 的 2 个子目录中,因此为什么在阵列上我从 [3] 开始

话虽如此,我的 .htaccess 文件如下:

RewriteEngine on
RewriteBase /
RewriteCond %REQUEST_FILENAME !-f
RewriteCond %REQUEST_FILENAME !-d
RewriteRule . sub/folder/index.php [QSA,L]

据我所知,这会获取sub/folder/ 之后写入的任何内容,并将页面直接重定向回 index.php,但是,它会掩盖地址栏中的 URL。

它唯一忽略这一点的是子目录是否确实存在。例如,我有一个文件夹/sub/folder/signup/,如果我要在地址栏中键入它,因为该目录存在,那么您不会被重定向到 index.php 文件,而是像往常一样发送到请求的目录。

现在在我的 index.php 文件上(记住我从 $uri[3] 开始,因为我在子文件夹中!)

$uri = $_SERVER['REQUEST_URI']; // This brings back /sub/folder/foo/bar/test/
$uri = explode("/", $uri); // Separate each one

$var_one = $uri[3]; // foo
$var_two = $uri[4]; // bar
$var_three = $uri[5]; // test

switch ($var_one) 

    case '':
    case 'home':
        include('home.php');
        exit();
        break;

    case 'signout':
    case 'logout':
        include('signout.php');
        exit();
        break;

    case 'dashboard':
    case 'dash':
        include('dashboard.php');
        exit();
    break;



// By Username
    $data_array = array(':username' => $var_one);
    $select_account = $connect->prepare("SELECT * FROM `users` WHERE `username` = :username");
    $select_account -> execute($data_array);
    $account_amount = $select_account->rowCount();
    if ($account_amount > 0)  include('profile.php'); exit(); 

// By Account ID
    $data_array = array(':id' => $var_one);
    $select_account = $connect->prepare("SELECT * FROM `users` WHERE `id` = :id");
    $select_account -> execute($data_array);
    $account_amount = $select_account->rowCount();
    if ($account_amount > 0)  include('profile.php'); exit(); 

include('page_not_found.php');

切换案例很简单,如果 url 是:/sub/folder/dashboard/,那么会显示dashboard.php。如果没有一个案例匹配,那么我们可能正在查看个人资料。第一个检查它是否可能是用户名,如果存在,则显示查看个人资料页面。然后,它会检查它是否可能是该配置文件的唯一 ID 号并执行相同的检查。

最后,如果其中任何一个都没有带回结果,那么我们会看到一个 404 page not found 页面。

如果是个人资料页面,然后我可以在 profile.php 文件中检查 $var_two 并查看他们是否以该名称上传了相册,例如 /sub/folder/joe/holiday/ 如果是,则运行查询获取所有内容,如果没有,显示消息/重定向或其他内容。

如果还有更多,请说出该文件夹 ($var_two) 中的特定图片 ($var_three),例如 /sub/folder/joe/holiday/beach/ - 然后通过显示结果的类似查询运行它。

这可能不是最好的方法,但它非常简单,而且一切都按照我的意愿进行,所以我真的不能抱怨。

【讨论】:

【参考方案4】:

下面是一个简单的例子:

.htaccess

RewriteEngine On
RewriteRule ^includes/.*$ index.php
RewriteCond %REQUEST_FILENAME !-f
RewriteRule ^(.*)$ index.php [QSA,L]

首先,您必须拒绝直接访问 .php 文件,您可以将它们放在单独的文件夹中,例如“/includes”,并将对该文件夹的任何调用重定向到 index.php。 其次,允许直接访问文件(如图像或 javascripts)。 最后一条规则,将其他任何内容重定向到 index.php。

PHP

基本上你必须有一套规则来测试 URL 和一些控制器来处理结果。

define( 'WEB_ROOT', rtrim( dirname($_SERVER["SCRIPT_NAME"]), '/' ) );
define( 'INCLUDES_ROOT', 'includes/' );

// examples of rewrite rules ( $key = action, $value = regular expression )
$rules = array( 
    'pages' => "/(?'page'dashboard|about|signin|signup)",   // e.g. '/about'
    'gallery' => "/(?'username'[\w\-]+)/gallery",   // e.g. '/some-user/gallery'
    'album' => "/(?'username'[\w\-]+)/(?'album'[\w\-]+)",   // e.g. '/some-user/some-album'
    'picture' => "/(?'username'[\w\-]+)/(?'album'[\w\-]+)/(?'picture'[\w\-]+)",     // e.g. '/some-user/some-album/some-picture'
    'home' => "/"   // e.g. '/'
    );

// get uri
$uri = '/' . trim( str_replace( WEB_ROOT, '', $_SERVER['REQUEST_URI'] ), '/' );

// test uri
foreach ( $rules as $action => $rule ) 
    $pattern = '/^'.str_replace( '/', '\/', $rule ).'$/';

    if ( preg_match( $pattern, $uri, $params ) ) 
        /* now you know the action and parameters so you can 
         * include appropriate template file ( or proceed in some other way )
         * NOTE: variable $params vill be visible in template ( use print_r( $params ) to see result )
         */
        include( INCLUDES_ROOT . $action . '.php' );
        // exit to avoid the 404 message 
        exit();
    


// nothing is found so handle 404 error
include( INCLUDES_ROOT . '404.php' );

下一步是检查接收到的参数。

【讨论】:

以上是关于用户友好的 URL - mod_rewrite 和 php 重定向的主要内容,如果未能解决你的问题,请参考以下文章

使用 mod_rewrite 实现具有多个变量的友好 URL 的最佳方法是啥?

如何使用 mod_rewrite 显示 SEO 友好的 URL?

使用 apache 的 mod_rewrite 来解析 SEO 友好的 URL

指向友好 URL 的 HTML 表单 [重复]

如何通过php查询使URL友好SEO [重复]

Apache 的 mod_rewrite VS。 PHP路由?