Steps to reproduce
- Create a custom rest resource (code example here)
- Enable the new rest resource with restui, allowing get, patch, post, and put HTTP verbs
- Set permissions permitting all users to access the resource
- Send 1 http request to your resource for each http verb. See that get, patch, and put all succeed. See that post fails with a message:
{
"message": "No route found for \"POST /todo/\": Method Not Allowed (Allow: PATCH, GET, PUT)"
}

Expected Result
That my custom resource would accept all http verbs, including post.
Actual result
get, patch, and put all work, but post fails with the error:
{
"message": "No route found for \"POST /todo/\": Method Not Allowed (Allow: PATCH, GET, PUT)"
}
Video Demonstration
http://www.youtube.com/watch?v=Hta2FhIrVMk
Relevant code
https://gist.github.com/MKorostoff/a1d8ad7f72f2d4fc051dd6dfc89eed6c
Comments
Comment #2
MKorostoff commentedComment #3
dawehnerCan you try to post to
/todo_rest_resource?$create_path = isset($definition['uri_paths']['https://www.drupal.org/link-relations/create']) ? $definition['uri_paths']['https://www.drupal.org/link-relations/create'] : '/' . strtr($this->pluginId, ':', '/');is the line that creates the needed route.Comment #4
MKorostoff commented@dawenhner, thanks, that works! I am able to post to
/todo_rest_resourceAs to the second point, regarding $create_path—where are you suggesting I put that line? I can see that line is already present in my base class at
\Drupal\rest\Plugin\ResourceBase::routes()Also, can you help me understand why get, patch, and put all respond on the url /todo but post (and only post) responds on the url /todo_rest_resource?
Comment #5
dawehner#2800871: Create a post method in Drupal 8: how to receive data? shows you can you use a different path for the POST route.
Comment #6
MKorostoff commentedLooking at this a little more closely, I actually suspect this is a bug. As you can see in
\Drupal\rest\Plugin\ResourceBase::routes(), when using the POST method, the path is set explicitly to the pluginID:The trouble is, POST is the only method that receives this treatment. GET, PATCH, and HEAD all use the 'canonical path.' I can't seem to think of a reason why it would ever be desirable to have POST requests sent to one URL, but all other request types sent to a different URL.
For my custom resource, it's easy enough to just override the
routes()method. However, I think this starts to look a little more serious is when accessing core resources. For instance, to read, update, or delete a node you must request/node/:nid. To create a node you must request/entity/node. I think this is pretty unintuitive, and I spent a long time figuring out how create nodes with Drupal rest for exactly this reason.The patch would be simple enough (just delete line 108 in the code block in my screenshot above). There's two things I worry about, however. First, is that I simply don't have the intimate familiarity with this code to say for sure that this wouldn't break something else. Second, I think it would be prudent to provide reverse compatibility so those using REST today do not suddenly have their resource urls change.
I've updated the issue title to reflect this new analysis.
Comment #7
dawehnerWell, its easy, GET/PATCH/HEAD ... all deal with a canonical URL, like the url to the entity you are saving. This one doesn't exist yet for newly created entities, which is the purpose of a POST request. Given that we need two different paths for them.
Comment #8
MKorostoff commented@dawehner yeah, of course PATCH and POST operations can't happen at the exact same URL. What I'm talking about is the base path, the part before the entity ID argument. If you GET a node from example.com/node/1, then you'd expect to be able to POST a node to example.com/node. At very least, I would expect for the first URL hunk to remain the same across all http verbs—something like /node/create would not be too unusual.
I don't think I have ever encountered a REST API that doesn't follow this pattern. This was the pattern in Drupal 7 restws as well as Drupal 7 services. Across a wide variety of products and languages, this is a very well established convention. Compare, for instance, these extremely popular REST APIs: stripe, twitter, google docs
Comment #9
wim leers#2293697: EntityResource POST routes all use the confusing default: use entity types' https://www.drupal.org/link-relations/create link template if available is related — it makes this sensible/logical for Entity REST resources.
Comment #10
wim leersTo answer #6: Yes, in all cases it uses the "canonical path", and only in the
POSTcase, it uses the "create path". You're saying the path is explicitly set to the plugin ID, but this is not true.Look at the code:
In other words:
POST, we override it:$route->setPath($create_path/{plugin_id}/{resource_id}, the default create path is/{plugin_id}So, as long as you don't specify a canonical path, all URLs will make sense. But … you did specify a canonical path:
So, if you want your "create path" (
POST) to match, then you need to change that to:Also: your canonical path looks incorrect, because it doesn't allow for an ID to be specified. It should probably be
/todo/{todo_id}. So you probably want something like:I trust/hope this answers all your questions. Thanks for reporting this!
So, I don't there is a bug in the code here. But, what clearly is lacking, is documentation. So, tagging for that, and working on that.
Comment #11
wim leersDocumentation written: https://www.drupal.org/node/2667736/revisions/view/9913841/10076979 — see the bottom of https://www.drupal.org/developing/api/8/rest.
Comment #12
wim leersComment #13
wim leersComment #14
MKorostoff commentedThanks! Looks great :-)
Comment #15
wim leersYou're very welcome! Glad you're happy with this solution :)
Comment #17
rudolfbykerDid this change in D9? Do we need to update the docs again?
Comment #18
erfekkes commentedin Drupal 9, the create link for POST operations has changed, as seen in ResourceBase.php
In order to get the correct uri for POST commands, now in the @RestResource annotation, in uri_paths, the key "create" has to be used referring to the uri - similar to the approach for the canonical uri.
Comment #19
anoopjohn commentedI think the public documentation has to be updated as well along with API documentation for Drupal 9. Couldn't find a reference to the change from
https://www.drupal.org/docs/drupal-apis/restful-web-services-api/restful...
https://api.drupal.org/api/drupal/core%21core.api.php/group/third_party/...
https://api.drupal.org/api/drupal/core%21modules%21rest%21src%21Annotati...
Not exactly sure where to document this change.