admin管理员组

文章数量:1295726

I am not sure if this is the right way to do it but I have a react site using WordPress API as the backend. I want to store my contact form entries in WordPress so I created a custom post type

 register_post_type('contact', array(
    'supports'      => array('title'),
    'has_archive'   => false,
    'show_in_rest'  => true,
    'public'        => true,
    'labels'        => array(
            'name'         => 'Contact DB',
            'add_new_item' => 'Add New Contact',
            'edit_item'    => 'Edit Contact', 
            'all_items'    => 'All Contacts',
    ),
    'menu_icon'     => 'dashicons-format-chat'
));

I am trying to keep it simple for now on the frontend and at least try store the title

  const onSubmit = async (data) => {
    try {
      const response = await axios.post(
        `${process.env.NEXT_PUBLIC_API_ENDPOINT}/contact`,
        {
          title: data.name,
        }
      );
    } catch (err) {
      console.log(err);
    }
  };

This should not be protected or require authorisation as It is just meant to collect contact form entries and a user doesn't need to be logged in to create records. However, I am getting a 401 error

{
    "code": "rest_cannot_create",
    "message": "Sorry, you are not allowed to create posts as this user.",
    "data": {
        "status": 401
    }
}

I am not sure if this is the right way to do it but I have a react site using WordPress API as the backend. I want to store my contact form entries in WordPress so I created a custom post type

 register_post_type('contact', array(
    'supports'      => array('title'),
    'has_archive'   => false,
    'show_in_rest'  => true,
    'public'        => true,
    'labels'        => array(
            'name'         => 'Contact DB',
            'add_new_item' => 'Add New Contact',
            'edit_item'    => 'Edit Contact', 
            'all_items'    => 'All Contacts',
    ),
    'menu_icon'     => 'dashicons-format-chat'
));

I am trying to keep it simple for now on the frontend and at least try store the title

  const onSubmit = async (data) => {
    try {
      const response = await axios.post(
        `${process.env.NEXT_PUBLIC_API_ENDPOINT}/contact`,
        {
          title: data.name,
        }
      );
    } catch (err) {
      console.log(err);
    }
  };

This should not be protected or require authorisation as It is just meant to collect contact form entries and a user doesn't need to be logged in to create records. However, I am getting a 401 error

{
    "code": "rest_cannot_create",
    "message": "Sorry, you are not allowed to create posts as this user.",
    "data": {
        "status": 401
    }
}
Share Improve this question asked Mar 26, 2021 at 18:42 user8463989user8463989 5931 gold badge8 silver badges24 bronze badges 10
  • Creating a post via the default endpoint (at /wp/v2/contact) requires authentication by default, i.e. in the API request, you need to include a user having the permission to create posts on the site, e.g. yourself. – Sally CJ Commented Mar 26, 2021 at 22:25
  • @SallyCJ ah I see, thank you. Is it possible to create a custom route that wouldn't require authentication (not sure if this is an option) – user8463989 Commented Mar 27, 2021 at 10:53
  • I think that since I am using react for the frontend I probably can't access that nonce on the server like I would with jQuery though as if this was a standard Wordpress setup? – user8463989 Commented Mar 27, 2021 at 10:59
  • 1 Sorry for the late reply.. and not sure if you still need help, but you could actually store the JWT token in a cache to avoid having to request the token each time you make your API requests. And yes, you can easily create custom REST API endpoints, but I think you should just use the default endpoint/route for your CPT. – Sally CJ Commented Mar 29, 2021 at 18:22
  • 1 Also, JWT is not the only method you can use to authenticate your REST API requests, and in fact, WordPress 5.6+ has its own application password which can be used from a remote app/site. :) – Sally CJ Commented Mar 29, 2021 at 18:23
 |  Show 5 more comments

1 Answer 1

Reset to default 3

I am not sure if this is the right way to do it

I want to store my contact form entries in WordPress so I created a custom post type

It's not the only right way, but IMO, it is a good approach. :)

It is just meant to collect contact form entries and a user doesn't need to be logged in to create records. However, I am getting a 401 error

As I said in the comments, creating a post via the default endpoint (e.g. at /wp/v2/contact in your case) requires authentication by default, i.e. in the API request, you need to associate a user having the permission to create posts on the site, e.g. yourself.

And if you visit the above link, the default authentication method is cookie-based which requires the current user be logged-in to your WordPress site, hence that method isn't suitable in your case since the contact form is available to everyone (registered/logged-in users and those who aren't).

So you might want to consider using application passwords instead, which in the past you'd need to use a plugin like this, but with WordPress 5.6+, you no longer need to use a 3rd-party plugin. :)

But I'm not going to talk further about what authentication method should you use or how should they be used, however, I still wanted to say that if I were to authenticate my API requests via JS and the current user is not logged-in to WordPress, I think I would not use JWT because it normally requires us to send our WordPress password when requesting for a token and we certainly don't want to expose our precious password! :)

This should not be protected or require authorisation

In that case, you can disable the authentication for your CPT's default API endpoint, and here's one easy way of doing it: Use a custom rest_controller_class for your CPT.

The steps:

  1. Extend the WP_REST_Posts_Controller class and modify the create_item_permissions_check() method (which checks if a given request has access to create a post) like so:

    // File name: class-my-wp-rest-contact-controller.php
    
    class My_WP_REST_Contact_Controller extends WP_REST_Posts_Controller {
        public function create_item_permissions_check( $request ) {
            if ( ! empty( $request['id'] ) ) {
                return new WP_Error(
                    'rest_post_exists',
                    __( 'Cannot create existing post.' ),
                    array( 'status' => 400 )
                );
            }
    
            return true;
        }
    }
    
  2. Then set the rest_controller_class to the name of the above class:

    // On 'init', load the above class file:
    require_once '/path/to/class-my-wp-rest-contact-controller.php';
    
    // Then register your CPT:
    register_post_type( 'contact', array(
        // ... your other args.
    
        'show_in_rest'          => true,
        'rest_controller_class' => 'My_WP_REST_Contact_Controller',
    
        // ... your other args.
    ) );
    

That's all, but feel free to futher customize the class (and just let me know if you need any clarification).

Update

Note that the above steps would only allow unauthenticated requests to create draft posts with basic post data like title. So basically, those steps would only help in getting rid of the rest_cannot_create error (note the "create" vs "publish" below).

To create published posts (post_status = publish), the request would need to be authenticated, and if not, then you'd get an error with the code rest_cannot_publish.

But instead of having to authenticate, you can extend the handle_status_param() method in the REST controller class like so:

public function handle_status_param( $post_status, $post_type ) {
    if ( in_array( $post_status, array( 'draft', 'publish' ) ) ) {
        return $post_status;
    }

    return parent::handle_status_param( $post_status, $post_type );
}

So in the above example, I'm allowing unauthenticated requests to set the post status to draft or publish, and for other statuses, I'm letting the parent class handle it.

And note that there are other permission checks done in the (parent) REST controller class, so you'd need to check the source for the relevant code should you get another error originating from that class, and then extend the method containing that code. (Remove, bypass or modify a restriction)

Additional Notes

  • If you intend on making the "contact" posts be published, then you should exclude the post type from search results by setting the exclude_from_search argument to true (or maybe just set public to false, then set both publicly_queryable and show_ui to true).

本文标签: rest apiReact post to WordPress custom post type