[API] Routes and Endpoints

https://developer.wordpress.org/rest-api/extending-the-rest-api/routes-and-endpoints/

API REST cung cấp cho chúng tôi một cách để đối sánh URI với các tài nguyên khác nhau trong cài đặt WordPress của chúng tôi. Theo mặc định, nếu bạn đã bật các liên kết cố định khá đẹp, thì API REST của WordPress sẽ “hoạt động” tại / wp-json /. Tại trang web WordPress của chúng tôi https://ourawesomesite.com, chúng tôi có thể truy cập chỉ mục của API REST bằng cách thực hiện yêu cầu GET tới https://ourawesomesite.com/wp-json/. Chỉ mục cung cấp thông tin về những tuyến nào có sẵn cho cài đặt WordPress cụ thể đó, cùng với những phương thức HTTP nào được hỗ trợ và những điểm cuối nào được đăng ký.

Routes vs Endpoints

Điểm cuối là các chức năng có sẵn thông qua API. Đây có thể là những việc như truy xuất chỉ mục API, cập nhật bài đăng hoặc xóa nhận xét. Các điểm cuối thực hiện một chức năng cụ thể, lấy một số tham số và trả về dữ liệu cho máy khách.

Tuyến đường là “tên” bạn sử dụng để truy cập các điểm cuối, được sử dụng trong URL. Một tuyến đường có thể có nhiều điểm cuối được liên kết với nó và điểm nào được sử dụng phụ thuộc vào động từ HTTP.

For example, with the URL http://example.com/wp-json/wp/v2/posts/123:

“Tuyến đường” là wp /v2 posts /123 - Tuyến đường không bao gồm wp-json vì wp-json là đường dẫn cơ sở cho chính API.

This route has 3 endpoints:

  • GET kích hoạt phương thức get_item, trả lại dữ liệu bài đăng cho máy khách.

  • PUT kích hoạt phương thức update_item, lấy dữ liệu để cập nhật và trả về dữ liệu bài đăng đã cập nhật.

  • DELETE kích hoạt phương thức delete_item, trả lại dữ liệu bài đăng hiện đã bị xóa cho khách hàng.

Trên các trang web không có liên kết cố định, thay vào đó, tuyến được thêm vào URL dưới dạng tham số rest_route. Đối với ví dụ trên, URL đầy đủ khi đó sẽ là http://example.com/?rest_route=/wp/v2/posts/1

Creating Endpoints

Nếu chúng tôi muốn tạo một điểm cuối trả về cụm từ “Xin chào Thế giới, đây là API REST của WordPress” khi nó nhận được yêu cầu GET, trước tiên chúng tôi cần đăng ký tuyến cho điểm cuối đó. Để đăng ký các tuyến, bạn nên sử dụng hàm register_rest_route (). Nó cần được gọi trên hook action rest_api_init. register_rest_route () xử lý tất cả ánh xạ cho các tuyến đường đến điểm cuối. Hãy cố gắng tạo một lộ trình “Hello World, đây là WordPress REST API”.

<?php
/**
 * This is our callback function that embeds our phrase in a WP_REST_Response
 */
function prefix_get_endpoint_phrase() {
  // rest_ensure_response() wraps the data we want to return into a WP_REST_Response, and ensures it will be properly returned.
  return rest_ensure_response('Hello World, this is the WordPress REST API');
}
/**
 * This function is where we register our routes for our example endpoint.
 */
function prefix_register_example_routes() {
  // register_rest_route() handles more arguments but we are going to stick to the basics for now.
  register_rest_route('hello-world/v1', '/phrase', array(
    // By using this constant we ensure that when the WP_REST_Server changes our readable endpoints will work as intended.
    'methods'  => WP_REST_Server::READABLE,
    // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
    'callback' => 'prefix_get_endpoint_phrase',
  ));
}
add_action('rest_api_init', 'prefix_register_example_routes');
?>

Đối số đầu tiên được truyền vào register_rest_route () là không gian tên, cung cấp cho chúng ta một cách để nhóm các tuyến của chúng ta. Đối số thứ hai được truyền vào là đường dẫn tài nguyên hoặc cơ sở tài nguyên. Ví dụ của chúng tôi, tài nguyên mà chúng tôi đang truy xuất là cụm từ “Hello World, đây là WordPress REST API”. Đối số thứ ba là một loạt các tùy chọn. Chúng tôi chỉ định phương thức nào mà điểm cuối có thể sử dụng và việc gọi lại nào sẽ xảy ra khi điểm cuối được khớp (có thể thực hiện nhiều việc hơn nhưng đây là các nguyên tắc cơ bản).

Đối số thứ ba cũng cho phép chúng tôi cung cấp một lệnh gọi lại quyền, có thể hạn chế quyền truy cập cho điểm cuối đối với một số người dùng nhất định. Đối số thứ ba cũng cung cấp một cách để đăng ký các đối số cho điểm cuối để các yêu cầu có thể sửa đổi phản hồi của điểm cuối của chúng ta. Chúng ta sẽ đi sâu vào các khái niệm đó trong phần điểm cuối của hướng dẫn này.

Khi chúng tôi truy cập https://ourawesomesite.com/wp-json/hello-world/v1/phrase, bây giờ chúng tôi có thể thấy API REST của chúng tôi chào đón chúng tôi một cách thân thiện. Chúng ta hãy xem xét các tuyến đường chuyên sâu hơn một chút.

Routes

Các tuyến trong API REST được đại diện bởi các URI. Bản thân tuyến đường là những gì được gắn vào cuối https://ourawesomesite.com/wp-json. Tuyến chỉ mục cho API là '/', đó là lý do tại sao https://ourawesomesite.com/wp-json/ trả về tất cả thông tin có sẵn cho API. Tất cả các tuyến đường nên được xây dựng trên tuyến đường này, phần wp-json có thể được thay đổi, nhưng nói chung, nên giữ nguyên.

Chúng tôi muốn đảm bảo rằng các tuyến đường của chúng tôi là duy nhất. Ví dụ, chúng ta có thể có một lộ trình cho những cuốn sách như thế này: / books. Lộ trình sách của chúng tôi hiện đã có tại https://ourawesomesite.com/wp-json/books. Tuy nhiên, đây không phải là một thực tiễn tốt vì chúng tôi sẽ làm ô nhiễm các tuyến đường tiềm năng cho API. Điều gì sẽ xảy ra nếu một plugin khác mà chúng tôi cũng muốn đăng ký một tuyến sách? Chúng tôi sẽ gặp rắc rối lớn trong trường hợp đó, vì hai tuyến đường sẽ xung đột với nhau và chỉ có thể sử dụng một tuyến. Tham số thứ tư của register_rest_route () là một boolean để biết liệu tuyến có nên ghi đè lên một tuyến hiện có hay không.

Tham số ghi đè cũng không thực sự giải quyết được vấn đề của chúng tôi, vì cả hai tuyến đều có thể ghi đè hoặc chúng tôi muốn sử dụng cả hai tuyến cho những việc khác nhau. Đây là nơi sử dụng không gian tên cho các tuyến đường của chúng tôi.

Namespaces

Điều cực kỳ quan trọng là thêm không gian tên vào các tuyến đường của bạn. Các điểm cuối "lõi" sử dụng không gian tên wp / v2.

Có một số điều chính cần lưu ý trong không gian tên điểm cuối cốt lõi. Phần đầu tiên của không gian tên là wp, đại diện cho tên nhà cung cấp; WordPress. Đối với các plugin của chúng tôi, chúng tôi sẽ muốn đưa ra các tên riêng cho cái mà chúng tôi gọi là phần nhà cung cấp của không gian tên. Trong ví dụ trên, chúng tôi đã sử dụng hello-world.

Theo sau phần nhà cung cấp là phần phiên bản của không gian tên. Các điểm cuối "cốt lõi" sử dụng v2 để đại diện cho phiên bản 2 của WordPress REST API. Nếu bạn đang viết một plugin, bạn có thể duy trì khả năng tương thích ngược của các điểm cuối API REST của mình bằng cách chỉ cần tạo các điểm cuối mới và tăng số phiên bản bạn cung cấp. Bằng cách này, cả hai điểm cuối v1 và v2 ban đầu đều có thể được truy cập.

Phần của tuyến theo sau không gian tên là đường dẫn tài nguyên.

Resource Paths

Đường dẫn tài nguyên phải biểu thị tài nguyên mà điểm cuối được liên kết. Trong ví dụ chúng tôi đã sử dụng ở trên, chúng tôi đã sử dụng cụm từ để biểu thị rằng tài nguyên mà chúng tôi đang tương tác là một cụm từ. Để tránh mọi va chạm, mỗi đường dẫn tài nguyên chúng ta đăng ký cũng phải là duy nhất trong một không gian tên. Đường dẫn tài nguyên nên được sử dụng để xác định các tuyến tài nguyên khác nhau trong một không gian tên nhất định.

Giả sử chúng tôi có một plugin xử lý một số chức năng Thương mại điện tử cơ bản. Chúng tôi sẽ có hai đơn đặt hàng loại tài nguyên chính và sản phẩm. Đơn đặt hàng là một yêu cầu đối với (các) sản phẩm nhưng bản thân chúng không phải là sản phẩm. Khái niệm tương tự áp dụng cho các sản phẩm. Mặc dù các tài nguyên này có liên quan với nhau nhưng chúng không giống nhau và mỗi tài nguyên nên sống trong một đường dẫn tài nguyên riêng biệt. Các tuyến đường của chúng tôi sẽ kết thúc giống như thế này cho plugin Thương mại điện tử của chúng tôi: / my-shop / v1 / order và / my-shop / v1 / products.

Sử dụng các tuyến như thế này, chúng tôi muốn mỗi tuyến trả về một tập hợp các đơn đặt hàng hoặc sản phẩm. Điều gì xảy ra nếu chúng tôi muốn lấy một sản phẩm cụ thể theo ID, chúng tôi sẽ cần sử dụng các biến đường dẫn trong các tuyến của mình.

Path Variables

Các biến đường dẫn cho phép chúng tôi thêm các tuyến đường động. Để mở rộng các tuyến Thương mại điện tử của chúng tôi, chúng tôi có thể đăng ký một tuyến để lấy các sản phẩm riêng lẻ.

<?php
/**
 * This is our callback function to return our products.
 *
 * @param WP_REST_Request $request This function accepts a rest request to process data.
 */
function prefix_get_products($request) {
  // In practice this function would fetch the desired data. Here we are just making stuff up.
  $products = array(
    '1' => 'I am product 1',
    '2' => 'I am product 2',
    '3' => 'I am product 3',
  );
  return rest_ensure_response($products);
}
/**
 * This is our callback function to return a single product.
 *
 * @param WP_REST_Request $request This function accepts a rest request to process data.
 */
function prefix_get_product($request) {
  // In practice this function would fetch the desired data. Here we are just making stuff up.
  $products = array(
    '1' => 'I am product 1',
    '2' => 'I am product 2',
    '3' => 'I am product 3',
  );
  // Here we are grabbing the 'id' path variable from the $request object. WP_REST_Request implements ArrayAccess, which allows us to grab properties as though it is an array.
  $id = (string) $request['id'];
  if (isset($products[$id])) {
    // Grab the product.
    $product = $products[$id];
    // Return the product as a response.
    return rest_ensure_response($product);
  } else {
    // Return a WP_Error because the request product was not found. In this case we return a 404 because the main resource was not found.
    return new WP_Error('rest_product_invalid', esc_html__('The product does not exist.', 'my-text-domain'), array('status' => 404));
  }
  // If the code somehow executes to here something bad happened return a 500.
  return new WP_Error('rest_api_sad', esc_html__('Something went horribly wrong.', 'my-text-domain'), array('status' => 500));
}
/**
 * This function is where we register our routes for our example endpoint.
 */
function prefix_register_product_routes() {
  // Here we are registering our route for a collection of products.
  register_rest_route('my-shop/v1', '/products', array(
    // By using this constant we ensure that when the WP_REST_Server changes our readable endpoints will work as intended.
    'methods'  => WP_REST_Server::READABLE,
    // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
    'callback' => 'prefix_get_products',
  ));
  // Here we are registering our route for single products. The (?P<id>[\d]+) is our path variable for the ID, which, in this example, can only be some form of positive number.
  register_rest_route('my-shop/v1', '/products/(?P<id>[\d]+)', array(
    // By using this constant we ensure that when the WP_REST_Server changes our readable endpoints will work as intended.
    'methods'  => WP_REST_Server::READABLE,
    // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
    'callback' => 'prefix_get_product',
  ));
}
add_action('rest_api_init', 'prefix_register_product_routes');
?>

May mắn thay, regex sẽ lọc ra bất cứ thứ gì không phải là số. Tuy nhiên, điều gì sẽ xảy ra nếu sản phẩm cho ID được yêu cầu không tồn tại. Chúng ta cần xử lý lỗi. Bạn có thể thấy cách cơ bản mà chúng tôi đang xử lý lỗi trong ví dụ mã ở trên. Khi bạn trả về một WP_Error trong lệnh gọi lại điểm cuối của mình, máy chủ API sẽ tự động xử lý việc gửi lỗi cho máy khách.

Mặc dù phần này là về các tuyến đường, chúng tôi đã đề cập khá kỹ về các điểm cuối. Các điểm cuối và các tuyến đường có mối quan hệ với nhau, nhưng chúng chắc chắn có sự khác biệt.

Endpoints

<?php
/**
 * This is our callback function to return our products.
 *
 * @param WP_REST_Request $request This function accepts a rest request to process data.
 */
function prefix_get_products($request) {
  // In practice this function would fetch the desired data. Here we are just making stuff up.
  $products = array(
    '1' => 'I am product 1',
    '2' => 'I am product 2',
    '3' => 'I am product 3',
  );
  return rest_ensure_response($products);
}
/**
 * This is our callback function to return a single product.
 *
 * @param WP_REST_Request $request This function accepts a rest request to process data.
 */
function prefix_create_product($request) {
  // In practice this function would create a product. Here we are just making stuff up.
  return rest_ensure_response('Product has been created');
}
/**
 * This function is where we register our routes for our example endpoint.
 */
function prefix_register_product_routes() {
  // Here we are registering our route for a collection of products and creation of products.
  register_rest_route('my-shop/v1', '/products', array(
    array(
      // By using this constant we ensure that when the WP_REST_Server changes, our readable endpoints will work as intended.
      'methods'  => WP_REST_Server::READABLE,
      // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
      'callback' => 'prefix_get_products',
    ),
    array(
      // By using this constant we ensure that when the WP_REST_Server changes, our create endpoints will work as intended.
      'methods'  => WP_REST_Server::CREATABLE,
      // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
      'callback' => 'prefix_create_product',
    ),
  ));
}
add_action('rest_api_init', 'prefix_register_product_routes');
?>

Chú ý: enum đã chỉ định những giá trị nào mà bộ lọc có thể đảm nhận.

Điểm cuối là điểm đến mà một tuyến đường cần phải ánh xạ đến. Đối với bất kỳ tuyến đường nhất định nào, bạn có thể có một số điểm cuối khác nhau được đăng ký cho nó. Chúng tôi sẽ mở rộng trên plugin Thương mại điện tử giả tưởng của mình, để thể hiện rõ hơn sự phân biệt giữa các tuyến và điểm cuối. Chúng tôi sẽ tạo hai điểm cuối tồn tại tại / wp-json / my-shop / v1 / products / route. Một điểm cuối sử dụng động từ HTTP GET để nhận sản phẩm và điểm cuối còn lại sử dụng động từ HTTP POST để tạo sản phẩm mới.

Tùy thuộc vào Phương thức HTTP mà chúng tôi sử dụng cho tuyến / wp-json / my-shop / v1 / products, chúng tôi được đối sánh với một điểm cuối khác và một lệnh gọi lại khác được kích hoạt. Khi chúng tôi sử dụng POST, chúng tôi kích hoạt gọi lại prefix_create_product () và khi chúng tôi sử dụng GET, chúng tôi kích hoạt gọi lại prefix_get_products ().

Có một số phương thức HTTP khác nhau và API REST có thể sử dụng bất kỳ phương thức nào trong số chúng.

HTTP Methods

Phương thức HTTP đôi khi được gọi là động từ HTTP. Chúng chỉ đơn giản là những cách khác nhau để giao tiếp qua HTTP. Những thứ chính được sử dụng bởi WordPress REST API là:

GET should be used for retrieving data from the API.
POST should be used for creating new resources (i.e users, posts, taxonomies).
PUT should be used for updating resources.
DELETE should be used for deleting resources.
OPTIONS should be used to provide context about our resources.

Điều quan trọng cần lưu ý là các phương pháp này không được hỗ trợ bởi mọi máy khách, vì chúng đã được giới thiệu trong HTTP 1.1. May mắn thay, API cung cấp một giải pháp cho những trường hợp đáng tiếc này. Nếu bạn muốn xóa một tài nguyên nhưng không thể gửi yêu cầu DELETE, thì bạn có thể sử dụng tham số _method hoặc tiêu đề X-HTTP-Method-Override trong yêu cầu của mình. Cách hoạt động của nó, bạn sẽ gửi yêu cầu ĐĂNG đến https://ourawesomesite.com/wp-json/my-shop/v1/products/1?_method=DELETE. Bây giờ bạn sẽ xóa sản phẩm số 1, mặc dù khách hàng của bạn không thể gửi phương thức HTTP thích hợp trong yêu cầu hoặc có thể đã có tường lửa chặn các yêu cầu DELETE.

Phương thức HTTP, kết hợp với định tuyến và các lệnh gọi lại, là những gì tạo nên cốt lõi của một điểm cuối.

Callbacks

Hiện chỉ có hai loại lệnh gọi lại cho các điểm cuối được API REST hỗ trợ; callback và allow_callback. Lệnh gọi lại chính phải xử lý tương tác với tài nguyên. Lệnh gọi lại quyền sẽ xử lý những gì người dùng có quyền truy cập vào điểm cuối. Bạn có thể thêm các cuộc gọi lại bổ sung bằng cách thêm thông tin bổ sung khi đăng ký một điểm cuối. Sau đó, bạn có thể móc vào các móc rest_pre_dispatch, rest_dispatch_request hoặc rest_post_dispatch để kích hoạt các lệnh gọi lại tùy chỉnh mới của mình.

Endpoint Callback

Lệnh gọi lại chính cho một điểm cuối xóa chỉ nên xóa tài nguyên và trả về một bản sao của nó trong phản hồi. Lệnh gọi lại chính cho điểm cuối tạo chỉ nên tạo tài nguyên và trả về phản hồi khớp với dữ liệu mới được tạo. Một lệnh gọi lại cập nhật chỉ nên sửa đổi các tài nguyên thực sự tồn tại. Một lệnh gọi lại đang đọc chỉ nên truy xuất dữ liệu đã tồn tại. Điều quan trọng là phải tính đến khái niệm về sự không đồng nhất.

Idempotence, trong ngữ cảnh của API REST, có nghĩa là nếu bạn thực hiện cùng một yêu cầu tới một điểm cuối, máy chủ sẽ xử lý yêu cầu theo cùng một cách. Hãy tưởng tượng nếu điểm cuối đọc của chúng ta không phải là không cố định. Bất cứ khi nào chúng tôi đưa ra yêu cầu, trạng thái của máy chủ của chúng tôi sẽ được sửa đổi theo yêu cầu, mặc dù chúng tôi chỉ cố gắng lấy dữ liệu. Điều này có thể là thảm họa. Bất kỳ lúc nào ai đó tìm nạp dữ liệu từ máy chủ của bạn, điều gì đó sẽ thay đổi trong nội bộ. Điều quan trọng là đảm bảo rằng việc đọc, cập nhật và xóa các điểm cuối không có tác dụng phụ khó chịu và chỉ bám sát những gì chúng dự định làm.

Trong một API REST, khái niệm về idmpotence được gắn với các phương thức HTTP thay vì gọi lại điểm cuối. Bất kỳ cuộc gọi lại nào bằng GET, HEAD, TRACE, OPTIONS, PUT hoặc DELETE đều không tạo ra bất kỳ tác dụng phụ nào. Yêu cầu POST không phải là không quan trọng và thường được sử dụng để tạo tài nguyên. Nếu bạn đã tạo một phương pháp tạo Idempotent thì bạn sẽ chỉ tạo một tài nguyên vì khi bạn thực hiện cùng một yêu cầu sẽ không có thêm tác dụng phụ nào đối với máy chủ. Để tạo, nếu bạn thực hiện cùng một yêu cầu lặp đi lặp lại, máy chủ sẽ tạo tài nguyên mới mỗi lần.

Để hạn chế việc sử dụng các điểm cuối, chúng ta cần đăng ký một lệnh gọi lại quyền.

Permissions Callback

Lệnh gọi lại quyền là cực kỳ quan trọng để bảo mật với WordPress REST API. Nếu bạn có bất kỳ dữ liệu riêng tư nào không được hiển thị công khai, thì bạn cần phải đăng ký quyền gọi lại cho các điểm cuối của mình. Dưới đây là ví dụ về cách đăng ký quyền gọi lại.

<?php
/**
 * This is our callback function that embeds our resource in a WP_REST_Response
 */
function prefix_get_private_data() {
  // rest_ensure_response() wraps the data we want to return into a WP_REST_Response, and ensures it will be properly returned.
  return rest_ensure_response('This is private data.');
}
/**
 * This is our callback function that embeds our resource in a WP_REST_Response
 */
function prefix_get_private_data_permissions_check() {
  // Restrict endpoint to only users who have the edit_posts capability.
  if (!current_user_can('edit_posts')) {
    return new WP_Error('rest_forbidden', esc_html__('OMG you can not view private data.', 'my-text-domain'), array('status' => 401));
  }
  // This is a black-listing approach. You could alternatively do this via white-listing, by returning false here and changing the permissions check.
  return true;
}
/**
 * This function is where we register our routes for our example endpoint.
 */
function prefix_register_example_routes() {
  // register_rest_route() handles more arguments but we are going to stick to the basics for now.
  register_rest_route('my-plugin/v1', '/private-data', array(
    // By using this constant we ensure that when the WP_REST_Server changes our readable endpoints will work as intended.
    'methods'             => WP_REST_Server::READABLE,
    // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
    'callback'            => 'prefix_get_private_data',
    // Here we register our permissions callback. The callback is fired before the main callback to check if the current user can access the endpoint.
    'permission_callback' => 'prefix_get_private_data_permissions_check',
  ));
}
add_action('rest_api_init', 'prefix_register_example_routes');
?>

Nếu bạn thử điểm cuối này mà không có bất kỳ Xác thực nào được bật thì bạn cũng sẽ nhận được phản hồi lỗi, ngăn bạn xem dữ liệu. Xác thực là một chủ đề lớn và cuối cùng một phần của chương này sẽ được tạo ra để hướng dẫn bạn cách tạo các quy trình xác thực của riêng bạn.

Arguments

Khi thực hiện yêu cầu tới một điểm cuối, bạn có thể cần chỉ định các tham số bổ sung để thay đổi phản hồi. Các tham số bổ sung này có thể được thêm vào trong khi đăng ký các điểm cuối. Hãy xem một ví dụ về cách sử dụng các đối số với một điểm cuối.

<?php
/**
 * This is our callback function that embeds our resource in a WP_REST_Response
 */
function prefix_get_colors($request) {
  // In practice this function would fetch the desired data. Here we are just making stuff up.
  $colors = array(
    'blue',
    'blue',
    'red',
    'red',
    'green',
    'green',
  );
  if (isset($request['filter'])) {
    $filtered_colors = array();
    foreach ($colors as $color) {
      if ($request['filter'] === $color) {
        $filtered_colors[] = $color;
      }
    }
    return rest_ensure_response($filtered_colors);
  }
  return rest_ensure_response($colors);
}
/**
 * We can use this function to contain our arguments for the example product endpoint.
 */
function prefix_get_color_arguments() {
  $args = array();
  // Here we are registering the schema for the filter argument.
  $args['filter'] = array(
    // description should be a human readable description of the argument.
    'description' => esc_html__('The filter parameter is used to filter the collection of colors', 'my-text-domain'),
    // type specifies the type of data that the argument should be.
    'type'        => 'string',
    // enum specified what values filter can take on.
    'enum'        => array('red', 'green', 'blue'),
  );
  return $args;
}
/**
 * This function is where we register our routes for our example endpoint.
 */
function prefix_register_example_routes() {
  // register_rest_route() handles more arguments but we are going to stick to the basics for now.
  register_rest_route('my-colors/v1', '/colors', array(
    // By using this constant we ensure that when the WP_REST_Server changes our readable endpoints will work as intended.
    'methods'  => WP_REST_Server::READABLE,
    // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
    'callback' => 'prefix_get_colors',
    // Here we register our permissions callback. The callback is fired before the main callback to check if the current user can access the endpoint.
    'args'     => prefix_get_color_arguments(),
  ));
}
add_action('rest_api_init', 'prefix_register_example_routes');
?>

Bây giờ chúng tôi đã chỉ định một đối số bộ lọc cho ví dụ này. Chúng ta có thể chỉ định đối số làm tham số truy vấn khi chúng ta yêu cầu điểm cuối. Nếu chúng tôi đưa ra yêu cầu GET tới https://ourawesomesitem.com/my-colors/v1/colors?filter=blue, chúng tôi sẽ chỉ được trả lại các màu xanh lam trong bộ sưu tập của mình. Bạn cũng có thể chuyển chúng dưới dạng tham số nội dung trong phần thân yêu cầu, thay vì trong chuỗi truy vấn. Để hiểu sự khác biệt giữa tham số truy vấn và tham số nội dung, bạn nên đọc về thông số HTTP. Tham số truy vấn nằm trong chuỗi truy vấn gắn với URL và tham số nội dung được nhúng trực tiếp vào nội dung của một yêu cầu HTTP.

Chúng tôi đã tạo một đối số cho điểm cuối của mình, nhưng làm cách nào để xác minh rằng đối số đó là một chuỗi và cho biết nó có khớp với giá trị đỏ, lục hoặc lam hay không. Để làm điều này, chúng ta cần chỉ định một lệnh gọi lại xác thực cho đối số của chúng ta.

Validation

Xác thực và làm sạch là cực kỳ quan trọng đối với bảo mật trong API. Gọi lại xác thực (trong WP 4.6+), kích hoạt trước khi gọi lại sanitize. Bạn nên sử dụng validate_callback cho các đối số của mình để xác minh xem đầu vào bạn đang nhận có hợp lệ hay không. Sanitize_callback nên được sử dụng để biến đổi đầu vào đối số hoặc xóa các phần không mong muốn khỏi đối số, trước khi đối số được xử lý bởi lệnh gọi lại chính.

Trong ví dụ trên, chúng ta cần xác minh rằng tham số bộ lọc là một chuỗi và nó khớp với giá trị đỏ, lục hoặc lam. Hãy xem mã trông như thế nào sau khi thêm vào validate_callback.

<?php
/**
 * This is our callback function that embeds our resource in a WP_REST_Response
 */
function prefix_get_colors($request) {
  // In practice this function would fetch more practical data. Here we are just making stuff up.
  $colors = array(
    'blue',
    'blue',
    'red',
    'red',
    'green',
    'green',
  );
  if (isset($request['filter'])) {
    $filtered_colors = array();
    foreach ($colors as $color) {
      if ($request['filter'] === $color) {
        $filtered_colors[] = $color;
      }
    }
    return rest_ensure_response($filtered_colors);
  }
  return rest_ensure_response($colors);
}
/**
 * Validate a request argument based on details registered to the route.
 *
 * @param  mixed            $value   Value of the 'filter' argument.
 * @param  WP_REST_Request  $request The current request object.
 * @param  string           $param   Key of the parameter. In this case it is 'filter'.
 * @return WP_Error|boolean
 */
function prefix_filter_arg_validate_callback($value, $request, $param) {
  // If the 'filter' argument is not a string return an error.
  if (!is_string($value)) {
    return new WP_Error('rest_invalid_param', esc_html__('The filter argument must be a string.', 'my-text-domain'), array('status' => 400));
  }
  // Get the registered attributes for this endpoint request.
  $attributes = $request->get_attributes();
  // Grab the filter param schema.
  $args = $attributes['args'][$param];
  // If the filter param is not a value in our enum then we should return an error as well.
  if (!in_array($value, $args['enum'], true)) {
    return new WP_Error('rest_invalid_param', sprintf(__('%s is not one of %s'), $param, implode(', ', $args['enum'])), array('status' => 400));
  }
}
/**
 * We can use this function to contain our arguments for the example product endpoint.
 */
function prefix_get_color_arguments() {
  $args = array();
  // Here we are registering the schema for the filter argument.
  $args['filter'] = array(
    // description should be a human readable description of the argument.
    'description'       => esc_html__('The filter parameter is used to filter the collection of colors', 'my-text-domain'),
    // type specifies the type of data that the argument should be.
    'type'              => 'string',
    // enum specified what values filter can take on.
    'enum'              => array('red', 'green', 'blue'),
    // Here we register the validation callback for the filter argument.
    'validate_callback' => 'prefix_filter_arg_validate_callback',
  );
  return $args;
}
/**
 * This function is where we register our routes for our example endpoint.
 */
function prefix_register_example_routes() {
  // register_rest_route() handles more arguments but we are going to stick to the basics for now.
  register_rest_route('my-colors/v1', '/colors', array(
    // By using this constant we ensure that when the WP_REST_Server changes our readable endpoints will work as intended.
    'methods'  => WP_REST_Server::READABLE,
    // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
    'callback' => 'prefix_get_colors',
    // Here we register our permissions callback. The callback is fired before the main callback to check if the current user can access the endpoint.
    'args'     => prefix_get_color_arguments(),
  ));
}
add_action('rest_api_init', 'prefix_register_example_routes');
?>

Sanitizing

Trong ví dụ trên, chúng ta không cần sử dụng sanitize_callback, vì chúng ta đang hạn chế đầu vào chỉ các giá trị trong enum của chúng ta. Nếu chúng tôi không xác thực chặt chẽ và chấp nhận bất kỳ chuỗi nào làm tham số, chúng tôi chắc chắn sẽ cần đăng ký sanitize_callback. Điều gì sẽ xảy ra nếu chúng tôi muốn cập nhật một trường nội dung và người dùng đã nhập một thứ gì đó như cảnh báo ('ZOMG Hacking you') ;. Giá trị trường có thể là một tập lệnh thực thi. Để loại bỏ dữ liệu không mong muốn hoặc để chuyển đổi dữ liệu thành định dạng mong muốn, chúng ta cần đăng ký sanitize_callback cho các đối số của mình. Dưới đây là một ví dụ về cách sử dụng sanitize_text_field () của WordPress để gọi lại sanitize:

<?php

/**
 * This is our callback function that embeds our resource in a WP_REST_Response.
 *
 * The parameter is already sanitized by this point so we can use it without any worries.
 */
function prefix_get_item($request) {
  if (isset($request['data'])) {
    return rest_ensure_response($request['data']);
  }

  return new WP_Error('rest_invalid', esc_html__('The data parameter is required.', 'my-text-domain'), array('status' => 400));
}

/**
 * Validate a request argument based on details registered to the route.
 *
 * @param  mixed            $value   Value of the 'filter' argument.
 * @param  WP_REST_Request  $request The current request object.
 * @param  string           $param   Key of the parameter. In this case it is 'filter'.
 * @return WP_Error|boolean
 */
function prefix_data_arg_validate_callback($value, $request, $param) {
  // If the 'data' argument is not a string return an error.
  if (!is_string($value)) {
    return new WP_Error('rest_invalid_param', esc_html__('The filter argument must be a string.', 'my-text-domain'), array('status' => 400));
  }
}

/**
 * Sanitize a request argument based on details registered to the route.
 *
 * @param  mixed            $value   Value of the 'filter' argument.
 * @param  WP_REST_Request  $request The current request object.
 * @param  string           $param   Key of the parameter. In this case it is 'filter'.
 * @return WP_Error|boolean
 */
function prefix_data_arg_sanitize_callback($value, $request, $param) {
  // It is as simple as returning the sanitized value.
  return sanitize_text_field($value);
}

/**
 * We can use this function to contain our arguments for the example product endpoint.
 */
function prefix_get_data_arguments() {
  $args = array();
  // Here we are registering the schema for the filter argument.
  $args['data'] = array(
    // description should be a human readable description of the argument.
    'description'       => esc_html__('The data parameter is used to be sanitized and returned in the response.', 'my-text-domain'),
    // type specifies the type of data that the argument should be.
    'type'              => 'string',
    // Set the argument to be required for the endpoint.
    'required'          => true,
    // We are registering a basic validation callback for the data argument.
    'validate_callback' => 'prefix_data_arg_validate_callback',
    // Here we register the validation callback for the filter argument.
    'sanitize_callback' => 'prefix_data_arg_sanitize_callback',
  );
  return $args;
}

/**
 * This function is where we register our routes for our example endpoint.
 */
function prefix_register_example_routes() {
  // register_rest_route() handles more arguments but we are going to stick to the basics for now.
  register_rest_route('my-plugin/v1', '/sanitized-data', array(
    // By using this constant we ensure that when the WP_REST_Server changes our readable endpoints will work as intended.
    'methods'  => WP_REST_Server::READABLE,
    // Here we register our callback. The callback is fired when this endpoint is matched by the WP_REST_Server class.
    'callback' => 'prefix_get_item',
    // Here we register our permissions callback. The callback is fired before the main callback to check if the current user can access the endpoint.
    'args'     => prefix_get_data_arguments(),
  ));
}

add_action('rest_api_init', 'prefix_register_example_routes');
?>

Last updated