使用重写API构建REST风格的URL

时间:2013-02-14 作者:kingkool68

我正在尝试为RESTful API生成重写规则。我只是想看看是否有比写下所有可能的重写组合更好的方法来完成这项工作。

好的,我在URL中有4个查询变量

指标国家响应调查的基本url为www.example。com/某些页面/4个变量的顺序将保持一致,但某些查询变量是可选的。

所以我可以。。。

/indicator/{indicator value}/country/{country value}/response/{response value}/survey/{survey value}/
或。。。(无/响应/)

/indicator/{indicator value}/country/{country value}/survey/{survey value}/
或。。。

/indicator/{indicator value}/country/{country value}/
有没有比过滤rewrite_rules_array 并添加手动创建的重写规则数组?将add_rewrite_endpoint() 重写\\u终结点或add_rewrite_tag() 对我有用吗?

1 个回复
最合适的回答,由SO网友:fuxia 整理而成

我认为最好的选择是端点。您可以将所有数据作为一个简单的字符串来获取,因此您可以决定如何解析它,并且不必担心与其他重写规则的冲突。

关于端点,我学到了一件事:让主要工作尽可能抽象,以数据不可知的方式修复WordPress API中的故障。

我将把逻辑分为三个部分:acontroller 选择模型和视图model 处理端点和一个或多个views 返回一些有用的数据或错误消息。

控制器让我们从控制器开始。它做的不多,所以我在这里使用一个非常简单的函数:

add_action( \'plugins_loaded\', \'t5_cra_init\' );

function t5_cra_init()
{
    require dirname( __FILE__ ) . \'/class.T5_CRA_Model.php\';

    $options = array (
        \'callback\' => array ( \'T5_CRA_View_Demo\', \'__construct\' ),
        \'name\'     => \'api\',
        \'position\' => EP_ROOT
    );
    new T5_CRA_Model( $options );
}
基本上,它加载模型T5_CRA_Model 交一些参数…还有所有的工作。控制器对模型或视图的内部逻辑一无所知。它只是把两者粘在一起。这是唯一不能重复使用的部分;这就是为什么我把它和其他部分分开。

现在我们至少需要两门课:model 注册API和view 创建输出。

该类车型将:

注册端点捕获在没有任何附加参数的情况下调用端点的情况,填充由于第三方代码中的某些错误而丢失的重写规则,修复WordPress与静态首页和端点的故障EP_ROOT

  • 将URI解析为一个数组(这也可以分开)
  • 使用这些值调用回调处理程序,我希望代码能说明问题。:)

    模型对数据的内部结构或表示形式一无所知。因此,您可以使用它注册数百个API,而无需更改一行。

    <?php  # -*- coding: utf-8 -*-
    /**
     * Register new REST API as endpoint.
     *
     * @author toscho http://toscho.de
     *
     */
    class T5_CRA_Model
    {
        protected $options;
    
        /**
         * Read options and register endpoint actions and filters.
         *
         * @wp-hook plugins_loaded
         * @param   array $options
         */
        public function __construct( Array $options )
        {
            $default_options = array (
                \'callback\' => array ( \'T5_CRA_View_Demo\', \'__construct\' ),
                \'name\'     => \'api\',
                \'position\' => EP_ROOT
            );
    
            $this->options = wp_parse_args( $options, $default_options );
    
            add_action( \'init\', array ( $this, \'register_api\' ), 1000 );
    
            // endpoints work on the front end only
            if ( is_admin() )
                return;
    
            add_filter( \'request\', array ( $this, \'set_query_var\' ) );
            // Hook in late to allow other plugins to operate earlier.
            add_action( \'template_redirect\', array ( $this, \'render\' ), 100 );
        }
    
        /**
         * Add endpoint and deal with other code flushing our rules away.
         *
         * @wp-hook init
         * @return void
         */
        public function register_api()
        {
            add_rewrite_endpoint(
                $this->options[\'name\'],
                $this->options[\'position\']
            );
            $this->fix_failed_registration(
                $this->options[\'name\'],
                $this->options[\'position\']
            );
        }
    
        /**
         * Fix rules flushed by other peoples code.
         *
         * @wp-hook init
         * @param string $name
         * @param int    $position
         */
        protected function fix_failed_registration( $name, $position )
        {
            global $wp_rewrite;
    
            if ( empty ( $wp_rewrite->endpoints ) )
                return flush_rewrite_rules( FALSE );
    
            foreach ( $wp_rewrite->endpoints as $endpoint )
                if ( $endpoint[0] === $position && $endpoint[1] === $name )
                    return;
    
            flush_rewrite_rules( FALSE );
        }
    
        /**
         * Set the endpoint variable to TRUE.
         *
         * If the endpoint was called without further parameters it does not
         * evaluate to TRUE otherwise.
         *
         * @wp-hook request
         * @param   array $vars
         * @return  array
         */
        public function set_query_var( Array $vars )
        {
            if ( ! empty ( $vars[ $this->options[\'name\'] ] ) )
                return $vars;
    
            // When a static page was set as front page, the WordPress endpoint API
            // does some strange things. Let\'s fix that.
            if ( isset ( $vars[ $this->options[\'name\'] ] )
                or ( isset ( $vars[\'pagename\'] ) and $this->options[\'name\'] === $vars[\'pagename\'] )
                or ( isset ( $vars[\'page\'] ) and $this->options[\'name\'] === $vars[\'name\'] )
                )
            {
                // In some cases WP misinterprets the request as a page request and
                // returns a 404.
                $vars[\'page\'] = $vars[\'pagename\'] = $vars[\'name\'] = FALSE;
                $vars[ $this->options[\'name\'] ] = TRUE;
            }
            return $vars;
        }
    
        /**
         * Prepare API requests and hand them over to the callback.
         *
         * @wp-hook template_redirect
         * @return  void
         */
        public function render()
        {
            $api = get_query_var( $this->options[\'name\'] );
            $api = trim( $api, \'/\' );
    
            if ( \'\' === $api )
                return;
    
            $parts  = explode( \'/\', $api );
            $type   = array_shift( $parts );
            $values = $this->get_api_values( join( \'/\', $parts ) );
            $callback = $this->options[\'callback\'];
    
            if ( is_string( $callback ) )
            {
                call_user_func( $callback, $type, $values );
            }
            elseif ( is_array( $callback ) )
            {
                if ( \'__construct\' === $callback[1] )
                    new $callback[0]( $type, $values );
                elseif ( is_callable( $callback ) )
                    call_user_func( $callback, $type, $values );
            }
            else
            {
                trigger_error(
                    \'Cannot call your callback: \' . var_export( $callback, TRUE ),
                    E_USER_ERROR
                );
            }
    
            // Important. WordPress will render the main page if we leave this out.
            exit;
        }
    
        /**
         * Parse request URI into associative array.
         *
         * @wp-hook template_redirect
         * @param   string $request
         * @return  array
         */
        protected function get_api_values( $request )
        {
            $keys    = $values = array();
            $count   = 0;
            $request = trim( $request, \'/\' );
            $tok     = strtok( $request, \'/\' );
    
            while ( $tok !== FALSE )
            {
                0 === $count++ % 2 ? $keys[] = $tok : $values[] = $tok;
                $tok = strtok( \'/\' );
            }
    
            // fix odd requests
            if ( count( $keys ) !== count( $values ) )
                $values[] = \'\';
    
            return array_combine( $keys, $values );
        }
    }
    
    视图现在我们必须对数据进行处理。我们还可以捕获不完整请求的缺失数据,或者将处理委托给其他视图或子控制器。

    下面是一个非常简单的示例:

    class T5_CRA_View_Demo
    {
        protected $allowed_types = array (
                \'plain\',
                \'html\',
                \'xml\'
        );
    
        protected $default_values = array (
            \'country\' => \'Norway\',
            \'date\'    => 1700,
            \'max\'     => 200
        );
        public function __construct( $type, $data )
        {
            if ( ! in_array( $type, $this->allowed_types ) )
                die( \'Your request is invalid. Please read our fantastic manual.\' );
    
            $data = wp_parse_args( $data, $this->default_values );
    
            header( "Content-Type: text/$type;charset=utf-8" );
            $method = "render_$type";
            $this->$method( $data );
        }
    
        protected function render_plain( $data )
        {
            foreach ( $data as $key => $value )
                print "$key: $value\\n";
        }
        protected function render_html( $data ) {}
        protected function render_xml( $data ) {}
    }
    
    重要的部分是:视图对端点一无所知。您可以使用它来处理完全不同的请求,例如wp-admin. 您可以将视图拆分为自己的MVC模式,或者只使用一个简单的函数。

  • 结束