WordPress REST API开发完全指南:自定义端点、JWT鉴权与前后端分离实战

WordPress内置的REST API让其不仅可以作为传统CMS使用,还可以作为Headless CMS为任何前端框架(Vue.js、React、Flutter App)提供数据接口。本文从自定义端点到前后端分离架构,给出完整的开发指南。

一、WordPress REST API基础

<code"># 默认REST API端点(无需任何配置)
GET  https://yourdomain.com/wp-json/wp/v2/posts          # 获取文章列表
GET  https://yourdomain.com/wp-json/wp/v2/posts/123     # 获取单篇文章
GET  https://yourdomain.com/wp-json/wp/v2/pages          # 页面列表
GET  https://yourdomain.com/wp-json/wp/v2/categories     # 分类列表
GET  https://yourdomain.com/wp-json/wp/v2/media          # 媒体文件列表

# 常用查询参数
GET /wp-json/wp/v2/posts?per_page=10&page=2             # 分页
GET /wp-json/wp/v2/posts?categories=5&orderby=date      # 按分类筛选
GET /wp-json/wp/v2/posts?search=关键词                   # 搜索
GET /wp-json/wp/v2/posts?_fields=id,title,excerpt,link  # 只返回指定字段

# 验证API可用性
curl https://yourdomain.com/wp-json/

二、注册自定义REST API端点

<code"><?php // 在插件或functions.php中注册自定义REST API add_action('rest_api_init', function() { // 端点1:热门文章列表(带浏览量) register_rest_route('myapi/v1', '/popular-posts', [ 'methods' => WP_REST_Server::READABLE,
        'callback'            => 'get_popular_posts_api',
        'permission_callback' => '__return_true',  // 公开接口
        'args'                => [
            'count' => [
                'default'           => 10,
                'validate_callback' => fn($v) => is_numeric($v) && $v > 0 && $v <= 50, 'sanitize_callback' => 'absint',
                'description'       => '返回数量(1~50)',
            ],
            'category' => [
                'default'           => 0,
                'sanitize_callback' => 'absint',
            ],
        ],
    ]);

    // 端点2:提交留言/表单(需要Nonce验证)
    register_rest_route('myapi/v1', '/contact', [
        'methods'             => WP_REST_Server::CREATABLE,
        'callback'            => 'submit_contact_form',
        'permission_callback' => '__return_true',
        'args'                => [
            'name'    => ['required' => true, 'sanitize_callback' => 'sanitize_text_field'],
            'email'   => ['required' => true, 'sanitize_callback' => 'sanitize_email'],
            'message' => ['required' => true, 'sanitize_callback' => 'sanitize_textarea_field'],
        ],
    ]);

    // 端点3:私有接口(需要JWT认证)
    register_rest_route('myapi/v1', '/user/orders', [
        'methods'             => WP_REST_Server::READABLE,
        'callback'            => 'get_user_orders',
        'permission_callback' => 'check_jwt_permission',  // JWT验证
    ]);

    // 端点4:修改内容(需要管理员权限)
    register_rest_route('myapi/v1', '/posts/(?P\d+)/feature', [
        'methods'             => WP_REST_Server::EDITABLE,
        'callback'            => 'toggle_post_featured',
        'permission_callback' => fn() => current_user_can('edit_posts'),
        'args'                => [
            'id'       => ['validate_callback' => fn($v) => is_numeric($v)],
            'featured' => ['required' => true, 'type' => 'boolean'],
        ],
    ]);
});

// 热门文章回调函数
function get_popular_posts_api(WP_REST_Request $request): WP_REST_Response {
    $count    = $request->get_param('count');
    $category = $request->get_param('category');

    $args = [
        'posts_per_page'  => $count,
        'post_status'     => 'publish',
        'meta_key'        => 'post_view_count',
        'orderby'         => 'meta_value_num',
        'order'           => 'DESC',
    ];
    if ($category) {
        $args['cat'] = $category;
    }

    $posts = get_posts($args);
    $data  = array_map(function($post) {
        return [
            'id'          => $post->ID,
            'title'       => get_the_title($post),
            'excerpt'     => get_the_excerpt($post),
            'url'         => get_permalink($post),
            'thumbnail'   => get_the_post_thumbnail_url($post->ID, 'medium'),
            'views'       => (int)get_post_meta($post->ID, 'post_view_count', true),
            'date'        => get_the_date('c', $post),
            'category'    => wp_list_pluck(get_the_category($post->ID), 'name'),
        ];
    }, $posts);

    return new WP_REST_Response([
        'success' => true,
        'count'   => count($data),
        'data'    => $data,
    ], 200);
}

// 联系表单回调
function submit_contact_form(WP_REST_Request $request): WP_REST_Response {
    // 速率限制(每个IP每小时最多5次)
    $ip  = $_SERVER['REMOTE_ADDR'];
    $key = "contact_rate:{$ip}";

    // 简单速率限制(需要Transients API)
    $count = (int)get_transient($key);
    if ($count >= 5) {
        return new WP_REST_Response(['error' => '提交过于频繁,请稍后再试'], 429);
    }
    set_transient($key, $count + 1, HOUR_IN_SECONDS);

    $name    = $request->get_param('name');
    $email   = $request->get_param('email');
    $message = $request->get_param('message');

    // 发送邮件给管理员
    wp_mail(
        get_option('admin_email'),
        "新联系表单:{$name}",
        "姓名:{$name}\n邮箱:{$email}\n\n内容:\n{$message}",
        ["Reply-To: {$name} <{$email}>"]
    );

    // 发送确认邮件给用户
    wp_mail($email, '您的留言已收到', "感谢您的联系,我们会在1~2个工作日内回复。");

    return new WP_REST_Response(['success' => true, 'message' => '留言已提交'], 200);
}

三、JWT鉴权配置

<code"># 安装JWT Authentication for WP REST API插件
wp plugin install jwt-authentication-for-wp-rest-api --activate

# 在wp-config.php中添加JWT密钥
define('JWT_AUTH_SECRET_KEY', '至少32位的随机字符串');
define('JWT_AUTH_CORS_ENABLE', true);
<code"># Nginx配置:允许Authorization请求头(JWT需要)
server {
    location / {
        # 允许前端携带Authorization头
        if ($request_method = OPTIONS) {
            add_header Access-Control-Allow-Origin $http_origin;
            add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
            add_header Access-Control-Allow-Headers "Authorization, Content-Type, X-WP-Nonce";
            add_header Access-Control-Max-Age 3600;
            return 204;
        }

        # 将Authorization头传递给PHP
        fastcgi_param HTTP_AUTHORIZATION $http_authorization;
        # ... 其他配置
    }
}
<code"># 获取JWT Token
curl -X POST https://yourdomain.com/wp-json/jwt-auth/v1/token \
    -H "Content-Type: application/json" \
    -d '{"username": "your_user", "password": "your_password"}'

# 返回:
# {"token": "eyJ0eXAiOi...", "user_email": "...", "user_display_name": "..."}

# 使用Token访问受保护端点
curl https://yourdomain.com/wp-json/myapi/v1/user/orders \
    -H "Authorization: Bearer eyJ0eXAiOi..."

四、CORS处理

<code"><?php
// 在functions.php中处理CORS(允许指定域名跨域访问)

add_action('rest_api_init', function() {
    remove_filter('rest_pre_serve_request', 'rest_send_cors_headers');

    add_filter('rest_pre_serve_request', function($value) {
        $allowed_origins = [
            'https://yourdomain.com',
            'https://app.yourdomain.com',
            'http://localhost:3000',    // 本地开发
        ];

        $origin = $_SERVER['HTTP_ORIGIN'] ?? '';

        if (in_array($origin, $allowed_origins)) {
            header("Access-Control-Allow-Origin: {$origin}");
            header("Access-Control-Allow-Credentials: true");
        } else {
            header("Access-Control-Allow-Origin: https://yourdomain.com");
        }

        header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
        header("Access-Control-Allow-Headers: Authorization, Content-Type, X-WP-Nonce");
        header("Vary: Origin");

        return $value;
    });
});

五、Vue.js前端对接示例

<code">// src/api/wordpress.js
import axios from 'axios';

const api = axios.create({
    baseURL: 'https://yourdomain.com/wp-json',
    timeout: 10000,
});

// 请求拦截器:自动附加JWT Token
api.interceptors.request.use(config => {
    const token = localStorage.getItem('jwt_token');
    if (token) {
        config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
});

// 响应拦截器:处理Token过期
api.interceptors.response.use(
    response => response.data,
    async error => {
        if (error.response?.status === 401) {
            localStorage.removeItem('jwt_token');
            window.location.href = '/login';
        }
        return Promise.reject(error);
    }
);

export const wpApi = {
    // 登录获取JWT Token
    async login(username, password) {
        const data = await api.post('/jwt-auth/v1/token', { username, password });
        localStorage.setItem('jwt_token', data.token);
        return data;
    },

    // 获取文章列表
    async getPosts(params = {}) {
        return api.get('/wp/v2/posts', {
            params: { _fields: 'id,title,excerpt,link,date,_embedded', _embed: 1, ...params }
        });
    },

    // 获取热门文章
    async getPopularPosts(count = 10) {
        return api.get('/myapi/v1/popular-posts', { params: { count } });
    },

    // 提交联系表单
    async submitContact(formData) {
        return api.post('/myapi/v1/contact', formData);
    },
};

六、API性能优化

<code"><?php // 1. 限制默认REST API返回的字段(减少数据传输量) add_filter('rest_post_query', function($args, $request) { // 强制限制每次请求最多返回50条 if (isset($args['posts_per_page']) && $args['posts_per_page'] > 50) {
        $args['posts_per_page'] = 50;
    }
    return $args;
}, 10, 2);

// 2. 缓存REST API响应(减少数据库查询)
add_filter('rest_pre_dispatch', function($result, $server, $request) {
    $route  = $request->get_route();
    $method = $request->get_method();

    // 只缓存GET请求的特定路由
    if ($method !== 'GET' || !preg_match('#^/wp/v2/posts#', $route)) {
        return $result;
    }

    $cache_key = 'rest_api_' . md5($route . serialize($request->get_params()));
    $cached = get_transient($cache_key);
    if ($cached !== false) {
        return $cached;
    }

    return $result;
}, 10, 3);

// 3. 禁用不需要的默认REST路由(减小攻击面)
add_filter('rest_endpoints', function($endpoints) {
    // 如果不需要用户枚举,移除users端点
    unset($endpoints['/wp/v2/users']);
    unset($endpoints['/wp/v2/users/(?P[\d]+)']);
    return $endpoints;
});

七、总结

WordPress REST API让WordPress从传统CMS转型为强大的内容后端(Headless CMS),配合Vue.js、React或Flutter可以构建现代化的前后端分离应用。自定义端点 + JWT鉴权 + CORS配置构成安全API的三要素。IDC.Net的香港VPS提供稳定的PHP + Nginx环境,是运行Headless WordPress的理想平台,CN2 GIA低延迟保障API响应速度。

THE END