I was testing D8.2.2 REST API with Commerce 2-beta3.

When I simply GET a Commerce Order, I receive an error in the Drupal log:

Symfony\Component\Routing\Exception\RouteNotFoundException: Route "entity.commerce_order_item.canonical" does not exist. in Drupal\Core\Routing\RouteProvider->getRouteByName() (line 187 of /var/www/html/core/lib/Drupal/Core/Routing/RouteProvider.php).

This is because Commerce Order Item Entity doesn't have a canonical path nor it is going to have one.

We need to add support for entities which don't have canonical path.

I had a discussion with on IRC, he suggested the bug is somewhere near:

\Drupal\rest\Plugin\Deriver\EntityDeriver::getDerivativeDefinitions

and

\Drupal\rest\Routing\ResourceRoutes::getRoutesForResourceConfig

Comments

skyredwang created an issue. See original summary.

skyredwang’s picture

Issue summary: View changes
Wim Leers’s picture

Component: routing system » entity system
Category: Bug report » Support request

Neither does core's \Drupal\file\Entity\File.

I don't see what the problem is? Some entities just don't have URLs, because they're not exposed to the world. That's a valid decision. What's the problem here?

dawehner’s picture

Well, the problem we have is actually that we make two decisions at the same time:

  1. Do we want to provide a HTML representation of the thing
  2. Do we want to expose the entity in the first place (for example via REST)

If you think about it, as you said before, its clear that a) doesn't have to be TRUE. On the other hand, for REST we need b). Now you probably argue,
sure, its totally enough to just use '/' . strtr($this->pluginId, ':', '/') . '/{id}' as URL for that entity, and we are fine. That's true, unless we are linking to those entities. In there we seem to use the URL to the entity rather than the REST URL to that particular entity.

skyredwang’s picture

What's the problem here?

The problem is that REST module will throw errors when they encounter entities without canonical path.

Wim Leers’s picture

Status: Active » Postponed (maintainer needs more info)

The problem is that REST module will throw errors when they encounter entities without canonical path.

The REST module explicitly is checking whether a given entity type has a canonical URL or not. So please provide steps to reproduce this.

dawehner’s picture

The REST module explicitly is checking whether a given entity type has a canonical URL or not. So please provide steps to reproduce this.

I assume some of the serializers don't do so.

chrisrikli’s picture

I'm seeing the same error when performing a POST to create a custom entity. Adding an entry to the module's routing.yml table solves the issue but...this seems like a hack. I'm hoping a more knowledgeable party can tell me if this is the "right" way to resolve this issue.

Berdir’s picture

Component: entity system » rest.module
Category: Support request » Bug report
Status: Postponed (maintainer needs more info) » Active

The REST module explicitly is checking whether a given entity type has a canonical URL or not. So please provide steps to reproduce this.

Something is definitely not doing that. you actually provided the best example file. It does *not* have a link template, but it overrides url() to do something custom. If we'd correctly check that, then we would *not* call $file->url() anymore and actually break that hack that was added there. That said, those url() calls are "interesting" because unlike toUrl(), they do not throw an exception, so not having one just results in an empty string atm.

Wim Leers’s picture

The code dealing with canonical URLs is not using the deprecated urlInfo('canonical') nor toUrl('canonical'). It's using $entity_type->getLinkTemplate('canonical'). See \Drupal\rest\Plugin\Deriver\EntityDeriver::getDerivativeDefinitions().

skyredwang’s picture

I made a test on File Entity with the steps below, but I cannot see a similar error on File Entity:

1. Fresh install D8.2.3
2. Add a file field to Article Content Type
3. Create an article node and filled with random info and uploaded two files in the file field
4. Enable REST and REST UI modules
5. GET /node/1?_format=json, then everything works fine.

skyredwang’s picture

Following #11, I was trying to make a second test with Order Entity from Drupal Commerce. Then, without making to REST step, I was able to reproduce the original error above.

6. Add an Order Item (entityreference) field to the Article Content Type
7. Create a random Order with random Order Items via Drupal Commerce Order management /admin/commerce/orders
8. Edit the article node created above, and use auto-complete to add an existing Order Item to the Article.
9. Clicking save, then WSOD, in the Drupal log, the original error was thrown.

Can this result prove the bug is not in REST module? But, somewhere else?

skyredwang’s picture

@Berdir showed me there is a error backtrace in D8

The website encountered an unexpected error. Please try again later.

Symfony\Component\Routing\Exception\RouteNotFoundException: Route "entity.commerce_order_item.canonical" does not exist. in Drupal\Core\Routing\RouteProvider->getRouteByName() (line 187 of core/lib/Drupal/Core/Routing/RouteProvider.php).
Drupal\Core\Routing\UrlGenerator->getRoute('entity.commerce_order_item.canonical') (Line: 277)
Drupal\Core\Routing\UrlGenerator->generateFromRoute('entity.commerce_order_item.canonical', Array, Array, 1) (Line: 105)
Drupal\Core\Render\MetadataBubblingUrlGenerator->generateFromRoute('entity.commerce_order_item.canonical', Array, Array, 1) (Line: 735)
Drupal\Core\Url->toString(1) (Line: 162)
Drupal\Core\Utility\LinkGenerator->generate('restTitle1', Object) (Line: 94)
Drupal\Core\Render\Element\Link::preRenderLink(Array)
call_user_func(Array, Array) (Line: 376)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 195)
Drupal\Core\Render\Renderer->render(Array) (Line: 474)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 128)
__TwigTemplate_dfb05fe89e5f1d11a12340506f57f727760c4efcec1410b067ce64fd3fd23314->doDisplay(Array, Array) (Line: 381)
Twig_Template->displayWithErrorHandling(Array, Array) (Line: 355)
Twig_Template->display(Array) (Line: 366)
Twig_Template->render(Array) (Line: 64)
twig_render_template('core/themes/classy/templates/field/field.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('field', Array) (Line: 435)
Drupal\Core\Render\Renderer->doRender(Array) (Line: 448)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 195)
Drupal\Core\Render\Renderer->render(Array) (Line: 474)
Drupal\Core\Template\TwigExtension->escapeFilter(Object, Array, 'html', NULL, 1) (Line: 114)
__TwigTemplate_f1f241934e13fc182a69a7f4fd4b833958df36f5a26e64db51cd330bcaaa08bc->doDisplay(Array, Array) (Line: 381)
Twig_Template->displayWithErrorHandling(Array, Array) (Line: 355)
Twig_Template->display(Array) (Line: 366)
Twig_Template->render(Array) (Line: 64)
twig_render_template('core/themes/bartik/templates/node.html.twig', Array) (Line: 384)
Drupal\Core\Theme\ThemeManager->render('node', Array) (Line: 435)
Drupal\Core\Render\Renderer->doRender(Array, ) (Line: 195)
Drupal\Core\Render\Renderer->render(Array, ) (Line: 226)
Drupal\Core\Render\MainContent\HtmlRenderer->Drupal\Core\Render\MainContent\{closure}() (Line: 574)
Drupal\Core\Render\Renderer->executeInRenderContext(Object, Object) (Line: 227)
Drupal\Core\Render\MainContent\HtmlRenderer->prepare(Array, Object, Object) (Line: 117)
Drupal\Core\Render\MainContent\HtmlRenderer->renderResponse(Array, Object, Object) (Line: 90)
Drupal\Core\EventSubscriber\MainContentViewSubscriber->onViewRenderArray(Object, 'kernel.view', Object) (Line: 111)
Drupal\Component\EventDispatcher\ContainerAwareEventDispatcher->dispatch('kernel.view', Object) (Line: 144)
Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object, 1) (Line: 62)
Symfony\Component\HttpKernel\HttpKernel->handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object, 1, 1) (Line: 99)
Drupal\page_cache\StackMiddleware\PageCache->pass(Object, 1, 1) (Line: 78)
Drupal\page_cache\StackMiddleware\PageCache->handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object, 1, 1) (Line: 50)
Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel->handle(Object, 1, 1) (Line: 652)
Drupal\Core\DrupalKernel->handle(Object) (Line: 19)
skyredwang’s picture

@Berdir suggested the #12 was caused by Entity Reference Field Formatter. So, I update the field display to be no link. Then, I was able to finish saving on Step 9.

Then, Step 10, use REST to GET /node/1?_format=json. I still see the same original error.

Wim Leers’s picture

Title: Not every entity has canonical path » OrderItem has 'canonical' link template but no route
Project: Drupal core » Commerce Core
Version: 8.2.x-dev » 8.x-2.x-dev
Component: rest.module » Order
Priority: Normal » Major

I'm pretty sure I found the root cause.

Note the error is:

Route "entity.commerce_order_item.canonical" does not exist

and not

Route "entity.commerce_order.canonical" does not exist

So it's not about the Order entity. It's about the OrderItem entity.


Looking at the current code:

/**
 * Defines the order entity class.
 *
 * @ContentEntityType(
 *   id = "commerce_order",
 *   label = @Translation("Order"),
…
 *   handlers = {
…
 *     "route_provider" = {
 *       "default" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
 *       "delete-multiple" = "Drupal\entity\Routing\DeleteMultipleRouteProvider",
 *     },
 *   },
…
 *   links = {
 *     "canonical" = "/admin/commerce/orders/{commerce_order}",
…
 * )
 */
class Order extends ContentEntityBase implements OrderInterface {

This has a canonical link and uses DefaultHtmlRouteProvider, which provides a canonical route.

Compare this with:

/**
 * Defines the order item entity class.
 *
 * @ContentEntityType(
 *   id = "commerce_order_item",
 *   label = @Translation("Order item"),
…
 *   handlers = {
 *     "event" = "Drupal\commerce_order\Event\OrderItemEvent",
 *     "storage" = "Drupal\commerce_order\OrderItemStorage",
 *     "access" = "Drupal\commerce\EmbeddedEntityAccessControlHandler",
 *     "views_data" = "Drupal\commerce_order\OrderItemViewsData",
 *     "form" = {
 *       "default" = "Drupal\Core\Entity\ContentEntityForm",
 *     },
 *     "inline_form" = "Drupal\commerce_order\Form\OrderItemInlineForm",
 *   },
…
 *   links = {
 *     "canonical" = "/admin/commerce/config/order-item/{commerce_order_item}",
…
 * )
 */
class OrderItem extends ContentEntityBase implements OrderItemInterface {

This has a canonical link but has no route providers. Hence it says it has a canonical link/URI, but it doesn't have the corresponding route!

skyredwang’s picture

Status: Active » Needs review

Following #15, I added

      "route_provider" = {
        "default" = "Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider",
      },

To the OrderItem definition.

Then, I am able to get the expected result for Step 10, and the original request on the top of this issue.

skyredwang’s picture

On the other hand, I also tested deleting this line:

"canonical" = "/admin/commerce/config/order-item/{commerce_order_item}",

It also fixed the problem, and REST works, too

Wim Leers’s picture

Yep, either #16 or #17 will work.

agoradesign’s picture

#17 is the right one. There's no need for a canonical link for order items

  • bojanz committed f02677f on 8.x-2.x
    Issue #2826470: OrderItem has 'canonical' link template but no route
    
bojanz’s picture

Status: Needs review » Fixed

Removed all of the links, they were years old and completely bogus.

Thank you for tracking this down!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.