网站开发

WordPress REST API 实战:让小程序和 APP 读你的网站数据

一个常见需求:客户想要个小程序,同时还要有官网。两套后台维护两份数据?太累。

更划算的做法:WordPress 当内容和数据的后台,小程序/App 通过接口读数据。内容只维护一份,两端同步。

WordPress 自带 REST API,开箱可用,不用装插件。这篇讲怎么用它。

一、先看看 API 长什么样

直接在浏览器打开:

https://你的域名/wp-json/wp/v2/posts

你会看到一堆 JSON 数据——这就是你最新的文章,机器可读的格式。

常用接口

接口 返回什么
/wp-json/wp/v2/posts 文章列表
/wp-json/wp/v2/posts/123 ID 为 123 的单篇文章
/wp-json/wp/v2/pages 页面
/wp-json/wp/v2/categories 分类
/wp-json/wp/v2/media 媒体文件
/wp-json/wp/v2/users 用户(注意:默认公开,见安全部分)

WooCommerce 也自带接口:/wp-json/wc/store/products(新版)或 /wp-json/wc/v3/products(旧版,需要密钥)。

常用参数

# 只要 5 条
/wp-json/wp/v2/posts?per_page=5

# 分页(第 2 页)
/wp-json/wp/v2/posts?page=2

# 按分类筛选(分类 ID 为 3)
/wp-json/wp/v2/posts?categories=3

# 搜索关键词
/wp-json/wp/v2/posts?search=wordpress

# 只要某些字段(减少传输量,很重要)
/wp-json/wp/v2/posts?_fields=id,title,link,excerpt

# 排序
/wp-json/wp/v2/posts?orderby=date&order=desc

_fields 参数对性能影响很大。默认一篇文章返回完整数据(含正文、所有 meta),几十 KB;只要几个字段能缩到 1KB 以内。移动端一定要用。

二、认证:读和写的区别

读取公开内容:不需要认证

已发布的文章、分类这些,任何人都能读,不需要登录。这对小程序场景刚好——访客看内容不需要身份。

写入数据(发布文章、下单):需要认证

三种方式:

方式 适合场景 复杂度
Application Passwords 小程序/App 后台操作 低(推荐)
JWT 插件 需要用户登录体系 中
Cookie + Nonce 同一站点内的 Ajax 请求 低(仅限本站)

Application Passwords(推荐给小程序场景)

后台 → 用户 → 你的账号 → 往下找到「应用程序密码」→ 生成一个。

会得到一串类似 xxxx xxxx xxxx xxxx xxxx xxxx 的密码(空格是为了好读,实际用的时候可以去掉)。

调用时:

curl -u "用户名:应用程序密码" \
  -X POST https://你的域名/wp-json/wp/v2/posts \
  -H "Content-Type: application/json" \
  -d '{"title":"测试标题","status":"draft"}'

两个前提:

  • 站点必须启用 HTTPS(WordPress 默认不允许在 HTTP 下使用 Application Passwords)
  • 这个密码等同于你的账号权限,泄露了别人就能以你的身份操作网站。不要写进小程序前端代码里

三、给 API 加自定义字段

默认的接口不包含你的自定义字段(比如 ACF 建的字段)。两种加法:

方法一:让 ACF 字段出现在 API 里

add_action( 'rest_api_init', function() {
    register_rest_field( 'post', 'acf_fields', array(
        'get_callback' => function( $post ) {
            return get_fields( $post['id'] );
        },
        'schema' => null,
    ) );
});

之后接口返回里会多一个 acf_fields 对象,包含所有自定义字段。

方法二:注册 CPT 时开启 REST 支持

register_post_type( 'case', array(
    'public'       => true,
    'show_in_rest' => true,          // 关键:这一行
    'rest_base'    => 'cases',       // 接口路径,默认用 CPT 名
    // ...
) );

加了 show_in_rest 之后,这个 CPT 就有自己的接口:/wp-json/wp/v2/cases。

自定义字段也要单独开:

register_post_meta( 'case', '_client_name', array(
    'show_in_rest' => true,
    'single'       => true,
    'type'         => 'string',
) );

四、自定义接口(写自己的业务逻辑)

有时候现成的接口不够用——比如你需要「一次性返回首页所有区块的数据」,减少请求次数。

add_action( 'rest_api_init', function() {
    register_rest_route( 'myapp/v1', '/home', array(
        'methods'             => 'GET',
        'callback'            => 'myapp_home_data',
        'permission_callback' => '__return_true',   // 公开访问
    ) );
});

function myapp_home_data() {
    $banners = get_posts( array(
        'post_type'      => 'banner',
        'posts_per_page' => 5,
    ) );

    $products = wc_get_products( array( 'limit' => 8 ) );

    return array(
        'banners'  => array_map( function( $p ) {
            return array(
                'title' => $p->post_title,
                'image' => get_the_post_thumbnail_url( $p->ID, 'full' ),
            );
        }, $banners ),
        'products' => array_map( function( $pr ) {
            return array(
                'id'    => $pr->get_id(),
                'name'  => $pr->get_name(),
                'price' => $pr->get_price(),
                'image' => wp_get_attachment_url( $pr->get_image_id() ),
            );
        }, $products ),
    );
}

访问 /wp-json/myapp/v1/home 就能拿到一次打包好的首页数据。对移动端性能友好——一个请求搞定,不用调七八次接口。

五、必须做的安全设置

接口开着,等于把你的数据暴露出来。有几件事必须做。

1. 关闭用户枚举(重要)

访问 /wp-json/wp/v2/users,默认会返回所有用户的用户名。攻击者拿到用户名就能针对性爆破密码。

add_filter( 'rest_endpoints', function( $endpoints ) {
    if ( isset( $endpoints['/wp/v2/users'] ) ) {
        unset( $endpoints['/wp/v2/users'] );
    }
    if ( isset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] ) ) {
        unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
    }
    return $endpoints;
});

2. 给写操作加权限检查

自定义接口里 permission_callback 不要无脑写 __return_true——那等于任何人都能调用。

'permission_callback' => function() {
    return current_user_can( 'publish_posts' );
},

3. 限制接口访问频率

接口被刷会导致服务器压力剧增。在 nginx 层做限流,或者用插件。

4. 敏感字段不要输出

返回数据前过滤一遍,别把内部字段(成本价、客户信息、内部备注)吐出去。尤其是电商站——接口里泄露成本价是真实发生过的事故。

六、性能:接口慢怎么办

接口慢的原因和页面慢一样,主要是数据库查询和 PHP 执行。

1. 用 _fields 减少数据量

前面提过,效果最明显。

2. 加缓存

add_action( 'rest_api_init', function() {
    register_rest_route( 'myapp/v1', '/home', array(
        'methods'  => 'GET',
        'callback' => function() {
            $cache = get_transient( 'myapp_home_data' );
            if ( $cache !== false ) {
                return $cache;
            }
            $data = myapp_build_home_data();
            set_transient( 'myapp_home_data', $data, 5 * MINUTE_IN_SECONDS );
            return $data;
        },
        'permission_callback' => '__return_true',
    ) );
});

用 transient 缓存 5 分钟,接口响应从几百毫秒降到几十毫秒。内容更新时清一下缓存即可。

3. 装 Redis 对象缓存

对 API 同样有效,尤其是列表接口。

七、什么时候不该用 WordPress REST API

诚实地说,WordPress 的 API 有它的局限:

  • 高并发场景——WordPress 每个请求都要加载完整内核,比专门的 API 框架重得多。日活几万的应用不建议用
  • 复杂的查询逻辑——WordPress 的查询参数比较基础,复杂的多表联查不如直接写 SQL 或者用专门的后端
  • 实时性要求高——比如聊天、实时数据,WordPress 不合适

适合它的场景:中小企业官网 + 小程序、内容型 App、内部工具、订单量不大的电商。这些场景下它省下的开发成本非常可观。

最后

用 WordPress 当后端,最大的价值不是技术多先进,而是:

  • 后台是现成的——不用从零开发管理界面,客户自己也就会用
  • 内容维护成本低——发文章、传图片都是成熟体验
  • 生态丰富——WooCommerce、多语言、SEO 插件都是现成的

对一个预算有限的中小项目来说,这个组合能省掉几万块的后台开发费用。而它多出来的那点性能开销,在这个量级下完全可以忽略。