Home > Mobile >  How to handle uncaught exceptions with the Wordpress REST API?
How to handle uncaught exceptions with the Wordpress REST API?

Time:09-17

Suppose I have a custom Wordpress REST API route and I throw an unhandled exception in the callback:

register_rest_route( $namespace, "/test", [
    "methods" => "get",
    "permission_callback" => "__return_true",
    "callback" => function( $req ) {
      throw new \Exception( "Error!!" );
    }
]);

If I query the route, Wordpress returns an HTML page that shows an error message, which is fine for themes or the admin area, but for the REST API I would rather return a WP_Error.

I guess I could put a try/catch block around the callback code, like this:

register_rest_route( $namespace, "/test", [
    "methods" => "get",
    "permission_callback" => "__return_true",
    "callback" => function( $req ) {
      try {
        throw new \Exception( "Error!!" );
      } catch( \Throwable $e ) {
        return new \WP_Error( "rest_error", $e->get_error_message() );
      }
    }
]);

But then I would have to do that for all my custom routes, right?

Is there any way that I can set a "global" default response for uncaught exceptions thrown in any custom endpoint?

CodePudding user response:

I've found a way to do it with set_exception_handler. Not sure if this is the best way, but it seems to work:

set_exception_handler( array( $this, "handle_uncaught_exceptions" ) );

public function handle_uncaught_exceptions( \Throwable $e ) {
   
   // First of all, log the exception
   
   log_from_exception( "errors", "alert", $e );
   
   // Now we have to return a response. By default, when an uncaught exception is thrown (fatal error) Wordpress returns 
   // an HTML page with an error message. That is fine eg. for a theme or the admin area, but for REST requests we'd rather
   // return a response that looks like a \WP_REST_Response
   
   if( is_rest_request() ) {
      
      // Set the response code to 500
      
      http_response_code(500);
      
      // Set the cross-domain headers
      
      header( "Access-Control-Allow-Origin: " . $_ENV["FRONTEND_URL"]; 
      header( "Access-Control-Allow-Credentials: true" ); 

      // Send the response
      
      $response = [
         "code" => "rest_fatal_error",
         "message" => "A fatal error occurred"
      ];
      
      wp_send_json( $response );
   
   } else {
      
      // For non-REST requests, we want to reset the default behavior, ie. sending an HTML page with an error message
      
      wp_die( $e->getMessage() );
   
   }
}

is_rest_request is a custom function:

function is_rest_request() {

   $uri = $_SERVER["REQUEST_URI"]; 

   if ( empty( $uri ) ) {
      return false;
   }
   
   $restPrefix = trailingslashit( rest_get_url_prefix() ); // Resolves to "wp-json/"
   $isRestApiRequest = strpos( $uri, $restPrefix ) !== false;         

   return $isRestApiRequest; 

}
  • Related