WordPress Gutenberg获取学生列表权限拒绝与Promise挂起问题
方案选择结论
选第二种自定义REST API路由的方案就行。第一种原生getUsers()的方案不是不能修,但是要改核心用户查询接口的权限,改完之后所有学生角色都能访问全站用户的查询接口,很容易泄露管理员、其他用户的敏感信息,安全风险太高。自定义路由可以自己控制返回字段、访问权限,灵活度和安全性都好很多。
第一种方案的问题(不推荐使用)
除了权限问题,你贴的第一种代码本身还有低级变量错误:定义的用户列表变量叫authors,后面加载判断、列表遍历全错写成了posts,就算权限放开也会直接报变量未定义。
如果硬要修这个方案,需要加钩子放开学生角色对用户接口的访问权限,但非常不建议这么做,等于给学生开了全站用户数据的查询入口,后续很容易出数据泄露问题。
第二种方案的问题修复
你之前的代码Promise一直pending、接口不通,是四个核心问题导致的:
- REST路由注册没挂到指定钩子,也没配权限回调,路由根本没生效
wp_localize_script绑错了脚本句柄,前端拿不到你传的nonce配置- 前端api-fetch的调用方式错了,已经import了apiFetch还去全局wp对象下找,nonce中间件配置不生效
- 接口回调没返回标准REST响应格式,也没做「排除当前登录用户」的逻辑
第一步:修复PHP端代码
注意把代码里的脚本句柄换成你自己项目里注册Gutenberg插件JS时用的句柄:
// 给Gutenberg编辑器脚本传接口配置 add_action( 'enqueue_block_editor_assets', function() { // 替换成你实际用wp_enqueue_script加载Gutenberg插件的脚本句柄 $script_handle = 'ccn-gut-script'; wp_localize_script( $script_handle, 'wpApiSettings', [ 'root' => esc_url_raw( rest_url() ), 'nonce' => wp_create_nonce( 'wp_rest' ) ]); } ); // 注册自定义接口,必须挂在rest_api_init钩子上 add_action( 'rest_api_init', 'student_api_rest' ); function student_api_rest() { register_rest_route( 'api/v1', '/students', [ 'methods' => 'GET', 'callback' => 'student_api_results', // 权限校验:登录用户即可访问,要限制仅学生访问就把判断改成in_array('student', wp_get_current_user()->roles) 'permission_callback' => function() { return is_user_logged_in(); } ] ); } // 接口查询逻辑 function student_api_results( $request ) { $current_uid = get_current_user_id(); // 直接在查询层排除当前登录用户,只返回需要的id和显示名字段,不返回敏感信息 $students = get_users( [ 'role' => 'student', 'fields' => [ 'ID', 'display_name' ], 'exclude' => [ $current_uid ] ] ); $format_data = []; foreach ( $students as $stu ) { $format_data[] = [ 'id' => $stu->ID, 'name' => $stu->display_name ]; } return rest_ensure_response( $format_data ); }
第二步:修复前端Gutenberg代码
把你原来的组件代码替换成下面的版本,修了变量错误、加了加载状态、补了空值兜底:
import { __ } from '@wordpress/i18n'; import { CheckboxControl, Spinner } from '@wordpress/components'; import { registerPlugin } from '@wordpress/plugins'; import { PluginDocumentSettingPanel } from '@wordpress/edit-post'; import { useSelect } from '@wordpress/data'; import { useEntityProp } from '@wordpress/core-data'; import { useState, useEffect } from '@wordpress/element'; import apiFetch from '@wordpress/api-fetch'; // 配置nonce校验 apiFetch.use( apiFetch.createNonceMiddleware( wpApiSettings.nonce ) ); const MetaboxStudents = () => { const postType = useSelect( select => select( 'core/editor' ).getCurrentPostType(), [] ); if ( postType !== 'subject-imposed' ) return null; const [ meta, setMeta ] = useEntityProp( 'postType', postType, 'meta' ); const [ students, setStudents ] = useState( null ); // 组件加载时拉取学生列表 useEffect( () => { apiFetch( { path: '/api/v1/students' } ) .then( res => setStudents( res ) ) .catch( err => console.error( '学生列表加载失败:', err ) ); }, [] ); // 复选框选值逻辑 const handleCheck = ( userId ) => { const checkedList = meta._metafield_students || []; setMeta( { _metafield_students: checkedList.includes( userId ) ? checkedList.filter( id => id !== userId ) : [ ...checkedList, userId ] } ); }; return ( <PluginDocumentSettingPanel name="list-students" title={ __( 'List of students', 'ccn-gut' ) } className="editor-styles-metabox" > <div className="gut-checkboxes-group"> { !students && <Spinner /> } { students?.map( user => ( <CheckboxControl label={ user.name } key={ `student-${user.id}` } checked={ (meta._metafield_students || []).includes( user.id ) } onChange={ () => handleCheck( user.id ) } /> ) ) } </div> </PluginDocumentSettingPanel> ); }; registerPlugin( 'plugin-document-students', { render: MetaboxStudents, icon: null } );
几个容易踩的小坑
- 确认你的文章类型
subject-imposed已经开启了custom-fields支持,不然meta字段存不进去 - 接口写完可以先直接在浏览器访问
你的域名/wp-json/api/v1/students,确认能正常返回数据再调试前端 - 如果nonce校验失败,去检查wp_localize_script绑定的脚本句柄是不是和你加载Gutenberg插件的句柄完全一致,句柄不对的话前端拿不到wpApiSettings变量,请求直接401
内容的提问来源于stack exchange,提问作者Jandon
相关产品推荐
相关产品推荐

