Intereting Posts

Использование API Rewrite для создания URL-адреса RESTful

Я пытаюсь создать правила перезаписи для RESTful API. Я просто хочу посмотреть, есть ли лучший способ сделать эту работу, чем написать все возможные комбинации перезаписи.

Итак, у меня есть 4 переменных запроса, которые нужно учитывать в URL-адресе

  • Индикатор
  • Страна
  • отклик
  • Опрос

Базовый URL-адрес будет следующим: www.example.com/some-page/. Порядок из 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() rewrite_endpoint или add_rewrite_tag() использоваться ко мне?

Я думаю, что лучший вариант – это конечная точка. Вы получаете все данные как простую строку, поэтому вы можете решить, как она будет анализироваться, и вам не придется беспокоиться о столкновениях с другими правилами перезаписи.

Одна вещь, которую я узнал о конечных точках: держите основную работу максимально абстрактной, исправляйте глюки в API WordPress агностически.

Я бы разделил логику на три части: контроллер выбирает модель и представление, модель для обработки конечной точки и одно или несколько представлений, чтобы возвращать некоторые полезные данные или сообщения об ошибках.

Контроллер

Начнем с контроллера. Это не делает много, поэтому я использую здесь очень простую функцию:

 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 и передает некоторые параметры … и всю работу. Контроллер ничего не знает о внутренней логике модели или представления. Он просто держится вместе. Это единственная часть, которую вы не можете повторно использовать; поэтому я оставил его отделенным от других частей.


Теперь нам нужны как минимум два класса: модель, которая регистрирует API и представление для создания вывода.

Модель

Этот класс будет:

  • зарегистрировать конечную точку
  • в которых конечная точка была вызвана без каких-либо дополнительных параметров
  • заполните правила перезаписи, которые отсутствуют из-за некоторых ошибок в стороннем коде
  • исправить EP_ROOT 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 ) {} } 

Важная часть: представление ничего не знает о конечной точке. Вы можете использовать его для обработки совершенно разных запросов, например запросов AJAX в wp-admin . Вы можете разделить представление на свой собственный шаблон MVC или использовать просто простую функцию.