我正在尝试在WordPress中创建自定义API端点,并且需要将请求重定向到WordPress根目录中的虚拟页面,并将其重定向到我的插件随附的实际页面。因此,基本上,对一个页面的所有请求实际上都被路由到另一页面。

示例:http://mysite.com/my-api.php => http://mysite.com/wp-content/plugins/my-plugin/my-api.php

重点是为API创建URL端点越短越好(类似于http://mysite.com/xmlrpc.php,但要随插件一起发送实际的API端点文件,而不是要求用户在其安装和/或hack内核中四处移动文件。

我的第一个stab是要添加一个自定义重写规则,但是,这有两个问题。


http://mysite.com/my-api.php/

我的重写规则

Idea?建议吗?仅被部分应用了,它不会重定向到wp-content/plugins...,而不会重定向到index.php&wp-content/plugins...,这导致WordPress显示页面未找到错误或仅默认为主页。

#1 楼

WordPress中有两种重写规则:内部规则(存储在数据库中,并由WP :: parse_request()解析)和外部规则(存储在.htaccess中,并由Apache解析)。您可以选择任何一种方式,具体取决于所调用文件中需要多少WordPress。

外部规则:

外部规则是最容易设置和遵循的规则。它将在您的插件目录中执行my-api.php,而无需从WordPress加载任何内容。

add_action( 'init', 'wpse9870_init_external' );
function wpse9870_init_external()
{
    global $wp_rewrite;
    $plugin_url = plugins_url( 'my-api.php', __FILE__ );
    $plugin_url = substr( $plugin_url, strlen( home_url() ) + 1 );
    // The pattern is prefixed with '^'
    // The substitution is prefixed with the "home root", at least a '/'
    // This is equivalent to appending it to `non_wp_rules`
    $wp_rewrite->add_external_rule( 'my-api.php$', $plugin_url );
}


内部规则:

内部规则需要做更多的工作:首先添加一个重写规则,该规则添加一个查询var,然后将该查询var公开,然后我们需要检查该查询var是否存在以将该控件传递给我们的插件文件。到我们这样做时,通常的WordPress初始化就会发生(我们在常规的帖子查询之前就中断了)。

add_action( 'init', 'wpse9870_init_internal' );
function wpse9870_init_internal()
{
    add_rewrite_rule( 'my-api.php$', 'index.php?wpse9870_api=1', 'top' );
}

add_filter( 'query_vars', 'wpse9870_query_vars' );
function wpse9870_query_vars( $query_vars )
{
    $query_vars[] = 'wpse9870_api';
    return $query_vars;
}

add_action( 'parse_request', 'wpse9870_parse_request' );
function wpse9870_parse_request( &$wp )
{
    if ( array_key_exists( 'wpse9870_api', $wp->query_vars ) ) {
        include 'my-api.php';
        exit();
    }
    return;
}


评论


我只想补充一点,很重要的一点是转到“永久链接”页面,然后在WP-Admin中单击“保存更改”。在考虑需要刷新永久链接之前,我已经玩了一个小时了……除非有人知道可以做到的功能?

–ethanpil
13年5月28日在18:21

对于外部规则:由于到我的Web根的路径具有空格字符,因此导致apache掉线。如果在您的wordpress安装路径中存在空白,则需要对其进行转义。

–威尔斯特
2013年6月4日9:41

可以,但是我似乎无法使用my-api.php中的get_query_vars()访问任何传递的查询变量。我检查了加载了哪些变量。唯一设置的变量是一个名为$ wp的WP对象。如何访问或转换为WP_Query对象,以便可以使用get_query_vars()访问传递的变量?

–法律
13年8月13日在10:24



@Jules:包含文件时,它将在当前作用域中执行。在这种情况下,它是wpse9870_parse_request函数,仅具有$ wp参数。可能此时尚未设置全局$ wp_query对象,因此get_query_var()将不起作用。但是,您很幸运:$ wp是包含所需的query_vars成员的类-我在上面的代码中亲自使用它。

– Jan Fabry
13年8月13日在15:49

试图创建一个外部重写规则。添加了您的第一段代码,但我仍然得到404。btw:刷新的重写规则

–西西尔
13年11月30日在8:36

#2 楼

这对我有用。我从未接触过重写API,但始终致力于将自己推向新的方向。以下内容适用于位于本地主机子文件夹中的3.0版测试服务器。我不希望在Web根目录中安装WordPress。

只需将此代码放在插件中,然后直接在插件文件夹中上传名为“ taco-kittens.php”的文件即可。您将需要为永久链接编写一个硬刷新。我认为他们说最好的时机是在插件激活上。

function taco_kitten_rewrite() {
    $url = str_replace( trailingslashit( site_url() ), '', plugins_url( '/taco-kittens.php', __FILE__ ) );
    add_rewrite_rule( 'taco-kittens\.php$', $url, 'top' );
}
add_action( 'wp_loaded', 'taco_kitten_rewrite' );


最好的祝愿,
-Mike

评论


我在尝试此代码时遇到访问被拒绝的错误。我怀疑我的服务器或WP不喜欢绝对URL。另一方面,这很好用:add_rewrite_rule('taco-kittens','wp-content / plugins / taco-kittens.php','top');

–法律
13年8月13日在9:46

您能告诉我,我应该放在taco-kittens.php中吗,我不了解.htaccess或url rewrite。

– Prafulla Kumar Sahu
16年5月4日在10:47

#3 楼

有什么理由不做这样的事情吗?

http://mysite.com/?my-api=1

然后将您的插件挂接到'init'并检查该获取变量。如果存在,请执行您的插件需要执行的操作并die()

评论


那会起作用,但是我试图在查询变量和实际端点之间提供一个非常清晰的区别。将来可能还会有其他查询参数,但我不希望用户混淆。

–EAMann
2011-02-20 4:53

如果您保留重写,但将其重写为GET var怎么办?您可能还会查看robots.txt的重写方式。它可能会帮助您弄清楚如何避免重定向到my-api.php /

–安德森(Will Anderson)
2011-2-20在5:34

如果您不喜欢机器人之类的API调用,那么这是一个完美的解决方案。

–贝塔洛夫斯基
17 Mar 11 '17 at 17:43

#4 楼

我可能无法完全理解您的问题,但是简单的短代码可以解决您的问题吗?

步骤:


让客户创建页面即
http://mysite.com/my-api
让客户端在该页面中添加一个短代码,即[my-api-shortcode]

新页面充当API端点并且您的简码会将请求发送到http://mysite.com/wp-content/plugins/my-plugin/my-api.php

(当然,这意味着my-api .php将定义短代码)

您可以通过插件自动执行步骤1和2。

#5 楼

我还没有处理太多的重写,所以这可能有点粗糙,但这似乎可以解决问题:

function api_rewrite($wp_rewrite) {
    $wp_rewrite->non_wp_rules['my-api\.php'] = 'wp-content/plugins/my-plugin/my-api.php';
    file_put_contents(ABSPATH.'.htaccess', $wp_rewrite->mod_rewrite_rules() );
}


如果将其钩住,它会起作用'generate_rewrite_rules',但是必须有更好的方法,因为您不想在每次加载页面时都重写.htaccess。
好像我不能停止编辑自己的帖子...进入您激活回调并引用全局$ wp_rewrite。然后从non_wp_rules中删除该条目,然后在停用回调的情况下再次输出到.htaccess。

最后,对.htaccess的编写应该更为复杂,您只想替换其中的wordpress部分在那里。

#6 楼

我有一个类似的要求,并希望基于指向插件生成的内容的唯一段创建几个端点。

看看我的插件的源:https:// wordpress。 org / extend / plugins / picasa-album-uploader /

我使用的技术首先为the_posts添加一个过滤器来检查传入的请求。如果插件可以处理它,则会生成一个虚拟帖子,并为template_redirect添加一个动作。

调用template_redirect动作时,它必须导致输出要显示的页面的全部内容,并且退出,否则它应返回且不生成任何输出。请参阅wp_include/template-loader.php中的代码,您会看到原因。

#7 楼

我正在使用另一种方法,该方法包括强制主页加载自定义标题,内容和页面模板。

该解决方案非常简洁,因为可以在用户遵循友好链接时实现该解决方案。作为http://example.com/?plugin_page=myfakepage

,它很容易实现,并且应允许无限制的页面。

此处的代码和说明:生成自定义/ fake /虚拟Wordpress页面进行实时浏览

#8 楼

我正在使用与上述Xavi Esteve类似的方法,据我所知,由于WordPress升级,该方法在2013年下半年停止了工作。

此处详细记录了该信息:
https://stackoverflow.com/questions/17960649/wordpress-plugin-generating-virtual-pages-and-using-theme-template

我方法的关键部分是使用现有模板因此结果页面看起来像是网站的一部分;我希望它与所有主题尽可能兼容,希望跨WordPress版本兼容。时间会证明我是否正确!

#9 楼

这是一个生产就绪示例,首先创建虚拟页面类:

 
class VirtualPage
{

    private $query;
    private $title;
    private $content;
    private $template;
    private $wp_post;

    function __construct($query = '/index2', $template = 'page', $title = 'Untitled')
    {
        $this->query = filter_var($query, FILTER_SANITIZE_URL);
        $this->setTemplate($template);
        $this->setTitle($title);
    }

    function getQuery()
    {
        return $this->query;
    }

    function getTemplate()
    {
        return $this->template;
    }

    function getTitle()
    {
        return $this->title;
    }

    function setTitle($title)
    {
        $this->title = filter_var($title, FILTER_SANITIZE_STRING);

        return $this;
    }

    function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    function setTemplate($template)
    {
        $this->template = $template;

        return $this;
    }

    public function updateWpQuery()
    {

        global $wp, $wp_query;

        // Update the main query
        $wp_query->current_post = $this->wp_post->ID;
        $wp_query->found_posts = 1;
        $wp_query->is_page = true;//important part
        $wp_query->is_singular = true;//important part
        $wp_query->is_single = false;
        $wp_query->is_attachment = false;
        $wp_query->is_archive = false;
        $wp_query->is_category = false;
        $wp_query->is_tag = false;
        $wp_query->is_tax = false;
        $wp_query->is_author = false;
        $wp_query->is_date = false;
        $wp_query->is_year = false;
        $wp_query->is_month = false;
        $wp_query->is_day = false;
        $wp_query->is_time = false;
        $wp_query->is_search = false;
        $wp_query->is_feed = false;
        $wp_query->is_comment_feed = false;
        $wp_query->is_trackback = false;
        $wp_query->is_home = false;
        $wp_query->is_embed = false;
        $wp_query->is_404 = false;
        $wp_query->is_paged = false;
        $wp_query->is_admin = false;
        $wp_query->is_preview = false;
        $wp_query->is_robots = false;
        $wp_query->is_posts_page = false;
        $wp_query->is_post_type_archive = false;
        $wp_query->max_num_pages = 1;
        $wp_query->post = $this->wp_post;
        $wp_query->posts = array($this->wp_post);
        $wp_query->post_count = 1;
        $wp_query->queried_object = $this->wp_post;
        $wp_query->queried_object_id = $this->wp_post->ID;
        $wp_query->query_vars['error'] = '';
        unset($wp_query->query['error']);

        $GLOBALS['wp_query'] = $wp_query;

        $wp->query = array();
        $wp->register_globals();

    }

    public function createPage()
    {
        if (is_null($this->wp_post)) {
            $post = new stdClass();
            $post->ID = -99;
            $post->ancestors = array(); // 3.6
            $post->comment_status = 'closed';
            $post->comment_count = 0;
            $post->filter = 'raw';
            $post->guid = home_url($this->query);
            $post->is_virtual = true;
            $post->menu_order = 0;
            $post->pinged = '';
            $post->ping_status = 'closed';
            $post->post_title = $this->title;
            $post->post_name = sanitize_title($this->template); // append random number to avoid clash
            $post->post_content = $this->content ?: '';
            $post->post_excerpt = '';
            $post->post_parent = 0;
            $post->post_type = 'page';
            $post->post_status = 'publish';
            $post->post_date = current_time('mysql');
            $post->post_date_gmt = current_time('mysql', 1);
            $post->modified = $post->post_date;
            $post->modified_gmt = $post->post_date_gmt;
            $post->post_password = '';
            $post->post_content_filtered = '';
            $post->post_author = is_user_logged_in() ? get_current_user_id() : 0;
            $post->post_content = '';
            $post->post_mime_type = '';
            $post->to_ping = '';

            $this->wp_post = new WP_Post($post);
            $this->updateWpQuery();

            @status_header(200);
            wp_cache_add(-99, $this->wp_post, 'posts');

        }


        return $this->wp_post;
    }
}
 


钩住template_redirect动作并像下面那样处理您的虚拟页面

     add_action( 'template_redirect', function () {


                    switch ( get_query_var( 'name' ,'') ) {

                        case 'contact':
                            // http://yoursite/contact  ==> loads page-contact.php
                            $page = new VirtualPage( "/contact", 'contact',__('Contact Me') );
                            $page->createPage();
                            break;

                        case 'archive':
                            // http://yoursite/archive  ==> loads page-archive.php
                            $page = new VirtualPage( "/archive", 'archive' ,__('Archives'));
                            $page->createPage();
                            break;

                        case 'blog':
                            // http://yoursite/blog  ==> loads page-blog.php
                            $page = new VirtualPage( "/blog", 'blog' ,__('Blog'));
                            $page->createPage();
                            break;


                }


            } );