Sử dụng API Rewrite để xây dựng URL RESTful


19

Tôi đang cố gắng tạo quy tắc viết lại cho API RESTful. Tôi chỉ muốn xem liệu có cách nào tốt hơn để thực hiện công việc này hơn là phải viết ra mọi kết hợp viết lại có thể.

Ok vì vậy tôi có 4 biến truy vấn để tính trong URL

  • Chỉ tiêu
  • Quốc gia
  • Phản ứng
  • Khảo sát

Url cơ sở sẽ là www.example.com/some-page/ Thứ tự của 4 biến sẽ nhất quán nhưng một số biến truy vấn là tùy chọn.

Vì vậy, tôi có thể có ...

/indicator/{indicator value}/country/{country value}/response/{response value}/survey/{survey value}/

hoặc ... (không / phản hồi /)

/indicator/{indicator value}/country/{country value}/survey/{survey value}/

hoặc là...

/indicator/{indicator value}/country/{country value}/

Có cách nào tốt hơn để thực hiện việc này hơn là lọc rewrite_rules_arrayvà thêm một mảng các quy tắc viết lại của tôi được tạo thủ công không? Sẽ add_rewrite_endpoint()viết lại_endpoint hoặc add_rewrite_tag()được sử dụng cho tôi?

Câu trả lời:


18

Tôi nghĩ rằng lựa chọn tốt nhất là một điểm cuối. Bạn nhận được tất cả dữ liệu dưới dạng một chuỗi đơn giản, do đó bạn có thể quyết định cách phân tích dữ liệu và bạn không phải lo lắng về việc va chạm với các quy tắc viết lại khác.

Một điều tôi học được về các điểm cuối: giữ cho công việc chính càng trừu tượng càng tốt, khắc phục các trục trặc trong API của WordPress theo cách không thể tin được về dữ liệu.

Tôi sẽ tách logic thành ba phần: bộ điều khiển chọn một mô hình và một khung nhìn, một mô hình để xử lý điểm cuối và một hoặc nhiều khung nhìn để trả về một số dữ liệu hữu ích hoặc thông báo lỗi.

Bộ điều khiển

Hãy bắt đầu với bộ điều khiển. Nó không làm được gì nhiều, vì vậy tôi sử dụng một chức năng rất đơn giản ở đây:

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 );
}

Về cơ bản, nó tải mô hình T5_CRA_Modelvà bàn giao một số thông số và tất cả các công việc. Bộ điều khiển không biết gì về logic bên trong của mô hình hoặc khung nhìn. Nó chỉ dính cả hai với nhau. Đây là phần duy nhất bạn không thể sử dụng lại; đó là lý do tại sao tôi giữ nó tách biệt với các phần khác.


Bây giờ chúng ta cần ít nhất hai lớp: mô hình đăng ký API và khung nhìn để tạo đầu ra.

Ngươi mâu

Lớp học này sẽ:

  • đăng ký điểm cuối
  • bắt các trường hợp có điểm cuối được gọi mà không có bất kỳ tham số bổ sung nào
  • điền quy tắc viết lại bị thiếu do một số lỗi trong mã bên thứ ba
  • sửa lỗi WordPress với các trang và điểm cuối tĩnh cho EP_ROOT
  • phân tích URI thành một mảng (điều này cũng có thể được tách ra)
  • gọi trình xử lý gọi lại với các giá trị đó

Tôi hy vọng mã nói cho chính nó. :)

Mô hình không biết gì về cấu trúc bên trong của dữ liệu hoặc về bản trình bày. Vì vậy, bạn có thể sử dụng nó để đăng ký hàng trăm API mà không cần thay đổi một dòng.

<?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 );
    }
}

Quan điểm

Bây giờ chúng tôi phải làm một cái gì đó với dữ liệu của chúng tôi. Chúng tôi cũng có thể bắt dữ liệu bị thiếu cho các yêu cầu không đầy đủ hoặc ủy quyền xử lý cho các chế độ xem hoặc bộ điều khiển phụ khác.

Đây là một ví dụ rất đơn giản:

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 ) {}
}

Phần quan trọng là: khung nhìn không biết gì về điểm cuối. Bạn có thể sử dụng nó để xử lý các yêu cầu hoàn toàn khác nhau, ví dụ như các yêu cầu AJAX trong wp-admin. Bạn có thể chia khung nhìn thành mẫu MVC của riêng nó hoặc chỉ sử dụng một chức năng đơn giản.


2
Đào nó Tôi thích loại mô hình này.
kingkool68
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.