我正在与您共享我为正在开发的插件创建的API,代码是开源的,可以根据您的需要进行修改。这应该给你一个基本的想法,让你开始。
这个API文件本身允许我远程查询服务器,当然,您需要一个db表来存储/验证API访问令牌和每个方法的类,例如获取用户、设置角色等。
<?php
if ( ! defined( \'ABSPATH\' ) ) {
exit; // Exit if accessed directly
}
/**
* MC_API class
*/
class MC_API {
private $pretty_print = false;
private $is_valid_request = false;
public $key_data = null;
public $permissions = null;
private $log_requests = true;
private $data = array();
public $endpoint;
public $api_vars;
/**
* Constructor
*/
public function __construct() {
add_action( \'init\', array( $this, \'add_endpoint\' ) );
add_action( \'wp\', array( $this, \'process_query\' ), -1 );
add_filter( \'query_vars\', array( $this, \'query_vars\' ) );
$this->pretty_print = defined( \'JSON_PRETTY_PRINT\' ) ? JSON_PRETTY_PRINT : null;
// Allow API request logging to be turned off
$this->log_requests = apply_filters( \'mc_api_log_requests\', $this->log_requests );
}
/**
* Add API endpoint
*/
public static function add_endpoint() {
add_rewrite_endpoint( \'mc-api\', EP_ALL );
}
/**
* Determines the kind of query requested and also ensure it is a valid query
*/
private function set_query_mode() {
global $wp_query;
// Whitelist our query options
$accepted = apply_filters( \'mc_api_valid_query_modes\', array(
\'get_user\',
\'update_user\',
\'update_usermeta\',
\'delete_user\',
\'bulk_delete_users\',
\'generate_key\',
\'revoke_key\',
\'get_permissions\',
\'set_role\',
\'set_credits\',
\'add_credits\',
\'deduct_credits\',
\'transfer_credits\',
\'set_user_status\',
\'friend_request\',
\'friend_cancel\',
\'friend_approve\',
\'friend_reject\',
\'friend_delete\',
\'follow\',
\'unfollow\',
\'get_info\'
) );
$query = isset( $wp_query->query_vars[\'mc-api\'] ) ? $wp_query->query_vars[\'mc-api\'] : null;
// Make sure our query is valid
if ( ! in_array( $query, $accepted ) ) {
$this->send_error( \'invalid_query\' );
}
$this->endpoint = $query;
}
/**
* Registers query vars for API access
*/
public function query_vars( $vars ) {
$this->api_vars = array(
\'format\',
\'consumer_key\',
\'consumer_secret\',
\'user\',
\'users\',
\'user1\',
\'user2\',
\'fields\',
\'values\',
\'id\',
\'limit\',
\'permissions\',
\'role\',
\'amount\',
\'status\'
);
$this->api_vars = apply_filters( \'mc_api_query_vars\', $this->api_vars );
foreach( $this->api_vars as $var ) {
$vars[] = $var;
}
return $vars;
}
/**
* Validate the API request
*/
private function validate_request() {
global $wp_query;
$consumer_key = isset( $wp_query->query_vars[\'consumer_key\'] ) ? $wp_query->query_vars[\'consumer_key\'] : null;
$consumer_secret = isset( $wp_query->query_vars[\'consumer_secret\'] ) ? $wp_query->query_vars[\'consumer_secret\'] : null;
if ( ! $consumer_key || ! $consumer_secret ) {
$this->send_error( \'missing_auth\' );
}
$user = $this->get_user_by_consumer_key( $consumer_key );
if ( ! $user ) {
$this->send_error( \'invalid_auth\', 401 );
}
// Compare provided hash with stored database hash
if ( ! hash_equals( $user->consumer_secret, $consumer_secret ) ) {
$this->send_error( \'invalid_auth\', 401 );
}
// Check that user did not exceed API limit
if ( $user->access_limit && $user->queries >= $user->access_limit ) {
$this->send_error( \'exceeded_limit\', 401 );
}
/**
* User does not have API manager capability, so we need to ensure that he is querying an endpoint that
* is possible with his API key permissions
*/
$can_read = array(
\'get_user\',
);
if ( $user->permissions == \'read\' && ! in_array( $this->endpoint, $can_read ) ) {
$this->send_error( \'invalid_permissions\', 401 );
}
// This is a valid request
$this->is_valid_request = true;
$this->key_data = $user;
$this->update_last_access();
}
/**
* Get user data and API key information by provided consumer key
*/
private function get_user_by_consumer_key( $consumer_key ) {
global $wpdb;
$user = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$wpdb->prefix}mc_api_keys WHERE consumer_key = %s", $consumer_key ) );
if ( is_object( $user ) ) {
$user->user_id = absint( $user->user_id );
$user->key_id = absint( $user->key_id );
$user->access_limit = absint( $user->access_limit );
$user->queries = absint( $user->queries );
}
return $user;
}
/**
* Updated API Key last access datetime.
*/
private function update_last_access() {
global $wpdb;
$key_id = $this->key_data->key_id;
$queries = $this->key_data->queries + 1;
$wpdb->query( $wpdb->prepare( "UPDATE {$wpdb->prefix}mc_api_keys SET last_access = %s, queries = %d WHERE key_id = %d", current_time( \'mysql\' ), $queries, $key_id ) );
}
/**
* Listens for the API and then processes the API requests
*/
public function process_query() {
global $wp_query;
// Check if user is not querying API
if ( ! isset( $wp_query->query_vars[\'mc-api\'] ) )
return;
// Check for API var. Get out if not present
if ( empty( $wp_query->query_vars[\'mc-api\'] ) ) {
$this->send_error( \'invalid_query\' );
}
// Determine the kind of query
$this->set_query_mode();
// Check for a valid user and set errors if necessary
$this->validate_request();
// Only proceed if no errors have been noted
if( ! $this->is_valid_request ) {
$this->send_error( \'invalid_auth\', 401 );
}
// Tell WP we are doing API request
if( ! defined( \'MC_DOING_API\' ) ) {
define( \'MC_DOING_API\', true );
}
// Need to collect $this->data before sending it
$data = array();
$class_name = str_replace(\' \', \'_\', ucwords( str_replace(\'_\', \' \', $this->endpoint ) ) );
$class_name = "MC_API_" . $class_name;
$data = new $class_name();
}
/**
* Before we send the data to output function
*/
public function send_data( $data ) {
global $wp_query, $wpdb;
$this->data = apply_filters( \'mc_api_output_data\', $data, $this->endpoint, $this );
// In case we do not have any data even after filtering
if ( count( (array) $this->data ) == 0 ) {
$this->data = array(
\'message\' => __( \'Your API request returned no data.\', \'mojocommunity\' ),
\'queried_endpoint\' => $this->endpoint
);
}
// Log this API request
$this->log_request();
$this->output();
}
/**
* Log a successful API request
*/
private function log_request() {
global $wp_query;
if ( ! $this->log_requests )
return;
$log = new MC_API_Log();
}
/**
* The query data is outputted as JSON by default
*/
private function output( $status_code = 200 ) {
$format = $this->get_output_format();
status_header( $status_code );
do_action( \'mc_api_output_before\', $this->data, $this, $format );
switch ( $format ) :
case \'json\' :
header( \'Content-Type: application/json\' );
if ( ! empty( $this->pretty_print ) )
echo json_encode( $this->data, $this->pretty_print );
else
echo json_encode( $this->data );
break;
default :
// Allow other formats to be added via extensions
do_action( \'mc_api_output_\' . $format, $this->data, $this );
break;
endswitch;
do_action( \'mc_api_output_after\', $this->data, $this, $format );
die();
}
/**
* Generate API key.
*/
public function generate_api_key( $args = array() ) {
global $wpdb;
$user_id = ( isset( $args[\'user_id\'] ) ) ? absint( $args[\'user_id\'] ) : null;
$description = ( isset( $args[\'description\'] ) ) ? $args[\'description\'] : __(\'Generated via the API\', \'mojocommunity\' );
$permissions = ( isset( $args[\'permissions\'] ) && in_array( $args[\'permissions\'], array( \'read\', \'write\', \'read_write\' ) ) ) ? $args[\'permissions\'] : \'read\';
$access_limit = ( isset( $args[\'access_limit\'] ) ) ? absint( $args[\'access_limit\'] ) : 0;
$consumer_key = \'ck_\' . mc_rand_hash();
$consumer_secret = \'cs_\' . mc_rand_hash();
$queries = 0;
if ( ! $user_id )
return false;
$data = array(
\'user_id\' => $user_id,
\'description\' => $description,
\'permissions\' => $permissions,
\'consumer_key\' => $consumer_key,
\'consumer_secret\' => $consumer_secret,
\'truncated_key\' => substr( $consumer_key, -7 ),
\'access_limit\' => $access_limit,
\'queries\' => $queries
);
$wpdb->insert(
$wpdb->prefix . \'mc_api_keys\',
$data,
array(
\'%d\',
\'%s\',
\'%s\',
\'%s\',
\'%s\',
\'%s\',
\'%d\',
\'%d\'
)
);
$data = array(
\'user_id\' => $user_id,
\'consumer_key\' => $consumer_key,
\'consumer_secret\' => $consumer_secret,
\'permissions\' => $permissions,
\'access_limit\' => $access_limit
);
return $data;
}
/**
* Revokes API access
*/
public function revoke_api_key( $key_id = 0 ) {
global $wpdb;
$key = $wpdb->get_row( $wpdb->prepare( "SELECT user_id, truncated_key FROM {$wpdb->prefix}mc_api_keys WHERE key_id = %d;", $key_id ) );
if ( ! $key ) {
return new MC_Error( \'invalid_api_key_id\', __( \'The specified API key identifier is invalid.\', \'mojocommunity\' ) );
}
$wpdb->query( $wpdb->prepare( "DELETE FROM {$wpdb->prefix}mc_api_keys WHERE key_id = %d;", $key->key_id ) );
$data = array(
\'user_id\' => $key->user_id,
\'consumer_key_ending_in\' => $key->truncated_key,
\'success\' => __( \'The API access has been revoked.\', \'mojocommunity\' )
);
return $data;
}
/**
* Retrieve the output format
*/
private function get_output_format() {
global $wp_query;
$format = isset( $wp_query->query_vars[\'format\'] ) ? $wp_query->query_vars[\'format\'] : \'json\';
return apply_filters( \'mc_api_output_format\', $format );
}
/**
* Returns a customized error message for the API query
*/
public function send_error( $error = \'\', $code = 400 ) {
switch( $error ) {
default :
break;
case \'invalid_query\' :
$error = __( \'The requested API method is invalid or missing parameters.\', \'mojocommunity\' );
break;
case \'missing_auth\' :
$error = __( \'Your API request could not be authenticated due to missing credentials.\', \'mojocommunity\' );
break;
case \'invalid_auth\' :
$error = __( \'Your API request could not be authenticated due to invalid credentials.\', \'mojocommunity\' );
break;
case \'exceeded_limit\' :
$error = __( \'You have exceeded your API usage limit for this key.\', \'mojocommunity\' );
break;
case \'invalid_permissions\' :
$error = __( \'Your API request could not be authenticated due to invalid permissions.\', \'mojocommunity\' );
break;
}
$this->data = array(
\'error\' => $error,
\'error_code\' => $code
);
$this->output( $code );
}
}
为了补充上述代码,这里有一个示例API调用/类,比如get\\u users query/endpoint。
/**
* MC_API_Get_User class
*/
class MC_API_Get_User {
/**
* Constructor
*/
public function __construct() {
global $wpdb, $wp_query;
$api = MC()->api;
$user = ( isset( $wp_query->query_vars[\'user\'] ) ) ? $wp_query->query_vars[\'user\'] : null;
$fields = ( isset( $wp_query->query_vars[\'fields\'] ) ) ? $wp_query->query_vars[\'fields\'] : null;
if ( ! $user ) {
$api->send_error( \'invalid_query\' );
}
$data = new MC_User( $user, $fields );
$api->send_data( $data );
}
}
请注意我是如何使用原始api中定义的$api->send\\u data()方法将数据作为json发送/输出的。我忘了说你需要
一个包含api密钥、访问限制、分配用户(基本内容)的表,如果您愿意记录请求详细信息,则可以使用自定义的帖子类型(最好知道谁偷偷进入您的api
The above code does multiple verifications which are
<确保用户提供真正有效的公钥和密钥。
验证是否未达到API密钥访问限制
确保API密钥具有执行指定任务的权限(读/写)。例如,您可以设置一个只读API键,该键可以只调用公共方法,例如获取用户信息,并生成另一个具有读写权限的键用于更新/编辑。
Here\'s the database table structure I use for storing API keys hopefully will save you some time doing your own:
CREATE TABLE {$wpdb->prefix}mc_api_keys (
key_id bigint(20) NOT NULL auto_increment,
user_id bigint(20) NOT NULL,
access_limit bigint(20) NOT NULL DEFAULT 0,
queries bigint(20) NOT NULL DEFAULT 0,
description longtext NULL,
permissions varchar(10) NOT NULL,
consumer_key char(64) NOT NULL,
consumer_secret char(64) NOT NULL,
truncated_key char(7) NOT NULL,
last_access datetime NULL DEFAULT null,
PRIMARY KEY (key_id),
KEY consumer_key (consumer_key),
KEY consumer_secret (consumer_secret)
) $collate;
这是logger类。负责将每个请求作为日志插入数据库。
/**
* MC_API_Log class
*/
class MC_API_Log {
protected $api = null;
public $log_id = 0;
/**
* Constructor
*/
public function __construct( $log_id = 0 ) {
$this->log_id = $log_id;
if ( $this->log_id > 0 ) {
$this->init_meta();
} else {
$this->api = MC()->api;
$this->send();
}
}
/**
* Init all post meta
*/
public function init_meta() {
$meta = get_post_meta( $this->log_id );
$this->consumer_key = ( isset( $meta[\'consumer_key\'][0] ) ) ? $meta[\'consumer_key\'][0] : null;
$this->key_id = ( isset( $meta[\'key_id\'][0] ) ) ? absint( $meta[\'key_id\'][0] ) : null;
$this->user_id = ( isset( $meta[\'user_id\'][0] ) ) ? absint( $meta[\'user_id\'][0] ) : 0;
$this->endpoint = ( isset( $meta[\'endpoint\'][0] ) ) ? $meta[\'endpoint\'][0] : null;
$this->user_ip = ( isset( $meta[\'user_ip\'][0] ) ) ? $meta[\'user_ip\'][0] : \'127.0.0.1\';
$this->time = get_the_time( \'j M Y g:ia\', $this->log_id );
}
/**
* Get truncated key
*/
public function get_key_html() {
return ( $this->consumer_key ) ? \'<a href="#">\' . \'…\' . esc_html( substr( $this->consumer_key, -7 ) ) . \'</a>\' : __( \'Invalid key\', \'mojocommunity\' );
}
/**
* Get user html
*/
public function get_user_html() {
$userdata = get_userdata( $this->user_id );
if ( false === $userdata ) {
return \'\';
}
return \'<a href="\' . get_edit_user_link( $this->user_id ) . \'">\' . $userdata->user_login . \'</a>\';
}
/**
* Get endpoint html
*/
public function get_endpoint_html() {
return ( $this->endpoint ) ? \'<code>\' . $this->endpoint . \'</code>\' : null;
}
/**
* Get ID
*/
public function get_id() {
return $this->log_id;
}
/**
* Get IP
*/
public function get_ip() {
return $this->user_ip;
}
/**
* Get date/time of a request
*/
public function get_time() {
return $this->time;
}
/**
* Insert the log
*/
public function insert_log() {
global $wp_query;
if ( ! empty( $this->error ) )
return;
$query = array();
foreach( $this->api->api_vars as $var ) {
if ( isset( $wp_query->query_vars[ $var ] ) && ! empty( $wp_query->query_vars[ $var ] ) && ! in_array( $var, array( \'consumer_key\', \'consumer_secret\' ) ) ) {
$query[ $var ] = $wp_query->query_vars[ $var ];
}
}
if ( http_build_query( $query ) ) {
$query = \'?\' . http_build_query( $query );
} else {
$query = null;
}
$this->post_args = apply_filters( \'mc_new_api_request_args\', array(
\'post_author\' => absint( $this->api->key_data->user_id ),
\'post_status\' => \'publish\',
\'post_type\' => \'log\',
\'comment_status\' => \'closed\'
) );
$this->post_meta = apply_filters( \'mc_new_api_request_meta\', array(
\'key_id\' => absint( $this->api->key_data->key_id ),
\'user_id\' => absint( $this->api->key_data->user_id ),
\'consumer_key\' => $this->api->key_data->consumer_key,
\'user_ip\' => mc_get_ip(),
\'endpoint\' => $this->api->endpoint . $query
) );
do_action( \'mc_before_insert_api_log\', $this->post_meta );
$this->log_id = mc_insert_post( $this->post_args, $this->post_meta );
do_action( \'mc_after_insert_api_log\', $this->log_id, $this->post_meta );
}
/**
* Send the log
*/
public function send() {
$this->insert_log();
}
}
这是
mc_insert_post
用于插入日志(或其他帖子类型)的函数
/**
* A wrapper function for inserting posts in database
*/
function mc_insert_post( $postarr = array(), $meta_input = array() ) {
do_action( \'mc_before_insert_post\', $postarr, $meta_input );
if ( ! empty( $meta_input ) && is_array( $meta_input ) )
$postarr = array_merge( $postarr, array( \'meta_input\' => $meta_input ) );
$post_id = wp_insert_post( $postarr );
do_action( \'mc_after_insert_post\', $post_id, $postarr, $meta_input );
return $post_id;
}