Changeset 48947
- Timestamp:
- 09/05/2020 09:50:31 PM (4 years ago)
- Location:
- trunk
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-includes/rest-api/class-wp-rest-server.php
r48576 r48947 912 912 } 913 913 914 $error = null; 915 $matched = $this->match_request_to_handler( $request ); 916 917 if ( is_wp_error( $matched ) ) { 918 return $this->error_to_response( $matched ); 919 } 920 921 list( $route, $handler ) = $matched; 922 923 if ( ! is_callable( $handler['callback'] ) ) { 924 $error = new WP_Error( 925 'rest_invalid_handler', 926 __( 'The handler for the route is invalid' ), 927 array( 'status' => 500 ) 928 ); 929 } 930 931 if ( ! is_wp_error( $error ) ) { 932 $check_required = $request->has_valid_params(); 933 if ( is_wp_error( $check_required ) ) { 934 $error = $check_required; 935 } else { 936 $check_sanitized = $request->sanitize_params(); 937 if ( is_wp_error( $check_sanitized ) ) { 938 $error = $check_sanitized; 939 } 940 } 941 } 942 943 return $this->respond_to_request( $request, $route, $handler, $error ); 944 } 945 946 /** 947 * Matches a request object to it's handler. 948 * 949 * @access private 950 * @since 5.6.0 951 * 952 * @param WP_REST_Request $request The request object. 953 * @return array|WP_Error The route and request handler on success or a WP_Error instance if no handler was found. 954 */ 955 public function match_request_to_handler( $request ) { 914 956 $method = $request->get_method(); 915 957 $path = $request->get_route(); … … 958 1000 959 1001 if ( ! is_callable( $callback ) ) { 960 $response = new WP_Error( 961 'rest_invalid_handler', 962 __( 'The handler for the route is invalid' ), 963 array( 'status' => 500 ) 964 ); 965 } 966 967 if ( ! is_wp_error( $response ) ) { 968 // Remove the redundant preg_match argument. 969 unset( $args[0] ); 970 971 $request->set_url_params( $args ); 972 $request->set_attributes( $handler ); 973 974 $defaults = array(); 975 976 foreach ( $handler['args'] as $arg => $options ) { 977 if ( isset( $options['default'] ) ) { 978 $defaults[ $arg ] = $options['default']; 979 } 1002 return array( $route, $handler ); 1003 } 1004 1005 $request->set_url_params( $args ); 1006 $request->set_attributes( $handler ); 1007 1008 $defaults = array(); 1009 1010 foreach ( $handler['args'] as $arg => $options ) { 1011 if ( isset( $options['default'] ) ) { 1012 $defaults[ $arg ] = $options['default']; 980 1013 } 981 982 $request->set_default_params( $defaults ); 983 984 $check_required = $request->has_valid_params(); 985 if ( is_wp_error( $check_required ) ) { 986 $response = $check_required; 987 } else { 988 $check_sanitized = $request->sanitize_params(); 989 if ( is_wp_error( $check_sanitized ) ) { 990 $response = $check_sanitized; 991 } 992 } 993 } 994 995 /** 996 * Filters the response before executing any REST API callbacks. 997 * 998 * Allows plugins to perform additional validation after a 999 * request is initialized and matched to a registered route, 1000 * but before it is executed. 1001 * 1002 * Note that this filter will not be called for requests that 1003 * fail to authenticate or match to a registered route. 1004 * 1005 * @since 4.7.0 1006 * 1007 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. Usually a WP_REST_Response or WP_Error. 1008 * @param array $handler Route handler used for the request. 1009 * @param WP_REST_Request $request Request used to generate the response. 1010 */ 1011 $response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request ); 1012 1013 if ( ! is_wp_error( $response ) ) { 1014 // Check permission specified on the route. 1015 if ( ! empty( $handler['permission_callback'] ) ) { 1016 $permission = call_user_func( $handler['permission_callback'], $request ); 1017 1018 if ( is_wp_error( $permission ) ) { 1019 $response = $permission; 1020 } elseif ( false === $permission || null === $permission ) { 1021 $response = new WP_Error( 1022 'rest_forbidden', 1023 __( 'Sorry, you are not allowed to do that.' ), 1024 array( 'status' => rest_authorization_required_code() ) 1025 ); 1026 } 1027 } 1028 } 1029 1030 if ( ! is_wp_error( $response ) ) { 1031 /** 1032 * Filters the REST dispatch request result. 1033 * 1034 * Allow plugins to override dispatching the request. 1035 * 1036 * @since 4.4.0 1037 * @since 4.5.0 Added `$route` and `$handler` parameters. 1038 * 1039 * @param mixed $dispatch_result Dispatch result, will be used if not empty. 1040 * @param WP_REST_Request $request Request used to generate the response. 1041 * @param string $route Route matched for the request. 1042 * @param array $handler Route handler used for the request. 1043 */ 1044 $dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler ); 1045 1046 // Allow plugins to halt the request via this filter. 1047 if ( null !== $dispatch_result ) { 1048 $response = $dispatch_result; 1049 } else { 1050 $response = call_user_func( $callback, $request ); 1051 } 1052 } 1053 1054 /** 1055 * Filters the response immediately after executing any REST API 1056 * callbacks. 1057 * 1058 * Allows plugins to perform any needed cleanup, for example, 1059 * to undo changes made during the {@see 'rest_request_before_callbacks'} 1060 * filter. 1061 * 1062 * Note that this filter will not be called for requests that 1063 * fail to authenticate or match to a registered route. 1064 * 1065 * Note that an endpoint's `permission_callback` can still be 1066 * called after this filter - see `rest_send_allow_header()`. 1067 * 1068 * @since 4.7.0 1069 * 1070 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. Usually a WP_REST_Response or WP_Error. 1071 * @param array $handler Route handler used for the request. 1072 * @param WP_REST_Request $request Request used to generate the response. 1073 */ 1074 $response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request ); 1075 1076 if ( is_wp_error( $response ) ) { 1077 $response = $this->error_to_response( $response ); 1078 } else { 1079 $response = rest_ensure_response( $response ); 1080 } 1081 1082 $response->set_matched_route( $route ); 1083 $response->set_matched_handler( $handler ); 1084 1085 return $response; 1086 } 1087 } 1088 1089 return $this->error_to_response( 1090 new WP_Error( 1091 'rest_no_route', 1092 __( 'No route was found matching the URL and request method' ), 1093 array( 'status' => 404 ) 1094 ) 1014 } 1015 1016 $request->set_default_params( $defaults ); 1017 1018 return array( $route, $handler ); 1019 } 1020 } 1021 1022 return new WP_Error( 1023 'rest_no_route', 1024 __( 'No route was found matching the URL and request method' ), 1025 array( 'status' => 404 ) 1095 1026 ); 1027 } 1028 1029 /** 1030 * Dispatches the request to the callback handler. 1031 * 1032 * @access private 1033 * @since 5.6.0 1034 * 1035 * @param WP_REST_Request $request The request object. 1036 * @param array $handler The matched route handler. 1037 * @param string $route The matched route regex. 1038 * @param WP_Error|null $response The current error object if any. 1039 * 1040 * @return WP_REST_Response 1041 */ 1042 public function respond_to_request( $request, $route, $handler, $response ) { 1043 /** 1044 * Filters the response before executing any REST API callbacks. 1045 * 1046 * Allows plugins to perform additional validation after a 1047 * request is initialized and matched to a registered route, 1048 * but before it is executed. 1049 * 1050 * Note that this filter will not be called for requests that 1051 * fail to authenticate or match to a registered route. 1052 * 1053 * @since 4.7.0 1054 * 1055 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. Usually a WP_REST_Response or WP_Error. 1056 * @param array $handler Route handler used for the request. 1057 * @param WP_REST_Request $request Request used to generate the response. 1058 */ 1059 $response = apply_filters( 'rest_request_before_callbacks', $response, $handler, $request ); 1060 1061 // Check permission specified on the route. 1062 if ( ! is_wp_error( $response ) && ! empty( $handler['permission_callback'] ) ) { 1063 $permission = call_user_func( $handler['permission_callback'], $request ); 1064 1065 if ( is_wp_error( $permission ) ) { 1066 $response = $permission; 1067 } elseif ( false === $permission || null === $permission ) { 1068 $response = new WP_Error( 1069 'rest_forbidden', 1070 __( 'Sorry, you are not allowed to do that.' ), 1071 array( 'status' => rest_authorization_required_code() ) 1072 ); 1073 } 1074 } 1075 1076 if ( ! is_wp_error( $response ) ) { 1077 /** 1078 * Filters the REST dispatch request result. 1079 * 1080 * Allow plugins to override dispatching the request. 1081 * 1082 * @since 4.4.0 1083 * @since 4.5.0 Added `$route` and `$handler` parameters. 1084 * 1085 * @param mixed $dispatch_result Dispatch result, will be used if not empty. 1086 * @param WP_REST_Request $request Request used to generate the response. 1087 * @param string $route Route matched for the request. 1088 * @param array $handler Route handler used for the request. 1089 */ 1090 $dispatch_result = apply_filters( 'rest_dispatch_request', null, $request, $route, $handler ); 1091 1092 // Allow plugins to halt the request via this filter. 1093 if ( null !== $dispatch_result ) { 1094 $response = $dispatch_result; 1095 } else { 1096 $response = call_user_func( $handler['callback'], $request ); 1097 } 1098 } 1099 1100 /** 1101 * Filters the response immediately after executing any REST API 1102 * callbacks. 1103 * 1104 * Allows plugins to perform any needed cleanup, for example, 1105 * to undo changes made during the {@see 'rest_request_before_callbacks'} 1106 * filter. 1107 * 1108 * Note that this filter will not be called for requests that 1109 * fail to authenticate or match to a registered route. 1110 * 1111 * Note that an endpoint's `permission_callback` can still be 1112 * called after this filter - see `rest_send_allow_header()`. 1113 * 1114 * @since 4.7.0 1115 * 1116 * @param WP_REST_Response|WP_HTTP_Response|WP_Error|mixed $response Result to send to the client. Usually a WP_REST_Response or WP_Error. 1117 * @param array $handler Route handler used for the request. 1118 * @param WP_REST_Request $request Request used to generate the response. 1119 */ 1120 $response = apply_filters( 'rest_request_after_callbacks', $response, $handler, $request ); 1121 1122 if ( is_wp_error( $response ) ) { 1123 $response = $this->error_to_response( $response ); 1124 } else { 1125 $response = rest_ensure_response( $response ); 1126 } 1127 1128 $response->set_matched_route( $route ); 1129 $response->set_matched_handler( $handler ); 1130 1131 return $response; 1096 1132 } 1097 1133 -
trunk/tests/phpunit/tests/rest-api/rest-server.php
r48939 r48947 1514 1514 } 1515 1515 1516 /** 1517 * @ticket 50244 1518 */ 1519 public function test_no_route() { 1520 $mock_hook = new MockAction(); 1521 add_filter( 'rest_request_after_callbacks', array( $mock_hook, 'filter' ) ); 1522 1523 $response = rest_do_request( '/test-ns/v1/test' ); 1524 $this->assertErrorResponse( 'rest_no_route', $response, 404 ); 1525 1526 // Verify that the no route error was not filtered. 1527 $this->assertCount( 0, $mock_hook->get_events() ); 1528 } 1529 1530 /** 1531 * @ticket 50244 1532 */ 1533 public function test_invalid_handler() { 1534 register_rest_route( 1535 'test-ns/v1', 1536 '/test', 1537 array( 1538 'callback' => 'invalid_callback', 1539 'permission_callback' => '__return_true', 1540 ) 1541 ); 1542 1543 $mock_hook = new MockAction(); 1544 add_filter( 'rest_request_after_callbacks', array( $mock_hook, 'filter' ) ); 1545 1546 $response = rest_do_request( '/test-ns/v1/test' ); 1547 $this->assertErrorResponse( 'rest_invalid_handler', $response, 500 ); 1548 1549 // Verify that the invalid handler error was filtered. 1550 $events = $mock_hook->get_events(); 1551 $this->assertCount( 1, $events ); 1552 $this->assertWPError( $events[0]['args'][0] ); 1553 $this->assertEquals( 'rest_invalid_handler', $events[0]['args'][0]->get_error_code() ); 1554 } 1555 1556 /** 1557 * @ticket 50244 1558 */ 1559 public function test_callbacks_are_not_executed_if_request_validation_fails() { 1560 $callback = $this->createPartialMock( 'stdClass', array( '__invoke' ) ); 1561 $callback->expects( self::never() )->method( '__invoke' ); 1562 $permission_callback = $this->createPartialMock( 'stdClass', array( '__invoke' ) ); 1563 $permission_callback->expects( self::never() )->method( '__invoke' ); 1564 1565 register_rest_route( 1566 'test-ns/v1', 1567 '/test', 1568 array( 1569 'callback' => $callback, 1570 'permission_callback' => $permission_callback, 1571 'args' => array( 1572 'test' => array( 1573 'validate_callback' => '__return_false', 1574 ), 1575 ), 1576 ) 1577 ); 1578 1579 $request = new WP_REST_Request( 'GET', '/test-ns/v1/test' ); 1580 $request->set_query_params( array( 'test' => 'world' ) ); 1581 $response = rest_do_request( $request ); 1582 1583 $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); 1584 } 1585 1586 /** 1587 * @ticket 50244 1588 */ 1589 public function test_filters_are_executed_if_request_validation_fails() { 1590 register_rest_route( 1591 'test-ns/v1', 1592 '/test', 1593 array( 1594 'callback' => '__return_empty_array', 1595 'permission_callback' => '__return_true', 1596 'args' => array( 1597 'test' => array( 1598 'validate_callback' => '__return_false', 1599 ), 1600 ), 1601 ) 1602 ); 1603 1604 $mock_hook = new MockAction(); 1605 add_filter( 'rest_request_after_callbacks', array( $mock_hook, 'filter' ) ); 1606 1607 $request = new WP_REST_Request( 'GET', '/test-ns/v1/test' ); 1608 $request->set_query_params( array( 'test' => 'world' ) ); 1609 $response = rest_do_request( $request ); 1610 1611 $this->assertErrorResponse( 'rest_invalid_param', $response, 400 ); 1612 1613 // Verify that the invalid param error was filtered. 1614 $events = $mock_hook->get_events(); 1615 $this->assertCount( 1, $events ); 1616 $this->assertWPError( $events[0]['args'][0] ); 1617 $this->assertEquals( 'rest_invalid_param', $events[0]['args'][0]->get_error_code() ); 1618 } 1619 1516 1620 public function _validate_as_integer_123( $value, $request, $key ) { 1517 1621 if ( ! is_int( $value ) ) {
Note: See TracChangeset
for help on using the changeset viewer.