diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php
index e5437cc..ca8318e 100644
--- a/core/modules/rest/src/RequestHandler.php
+++ b/core/modules/rest/src/RequestHandler.php
@@ -2,8 +2,10 @@
 
 namespace Drupal\rest;
 
+use Drupal\Component\Utility\ArgumentsResolver;
 use Drupal\Core\Cache\CacheableResponseInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Symfony\Component\DependencyInjection\ContainerAwareInterface;
@@ -119,17 +121,16 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
 
     // Determine the request parameters that should be passed to the resource
     // plugin.
-    $route_parameters = $route_match->getParameters();
-    $parameters = [];
-    // Filter out all internal parameters starting with "_".
-    foreach ($route_parameters as $key => $parameter) {
-      if ($key{0} !== '_') {
-        $parameters[] = $parameter;
-      }
+    $argument_resolver = $this->getArgumentResolver($route_match, $unserialized, $request);
+    try {
+      $arguments = $argument_resolver->getArguments([$resource, $method]);
+    }
+    catch (\RuntimeException $exception) {
+      $arguments = $this->getLegacyParameters($route_match, $unserialized, $request);
     }
 
     // Invoke the operation on the resource plugin.
-    $response = call_user_func_array([$resource, $method], array_merge($parameters, [$unserialized, $request]));
+    $response = $response = call_user_func_array([$resource, $method], $arguments);
 
     if ($response instanceof CacheableResponseInterface) {
       // Add rest config's cache tags.
@@ -139,4 +140,96 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
     return $response;
   }
 
+  /**
+   * Creates an argument resolver, which can pass along the REST parameters.
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match
+   * @param mixed $unserialized
+   *   The unserialized data.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
+   * @return \Drupal\Component\Utility\ArgumentsResolver
+   */
+  protected function getArgumentResolver(RouteMatchInterface $route_match, $unserialized, Request $request) {
+    $route = $route_match->getRouteObject();
+
+    // Defaults for the parameters defined on the route object need to be added
+    // to the raw arguments.
+    $raw_route_arguments = $route_match->getRawParameters()->all() + $route->getDefaults();
+
+    $route_arguments = $route_match->getParameters()->all();
+    $upcasted_route_arguments = $route_arguments;
+
+    // \Drupal\rest\Plugin\ResourceInterface plugins historically receive the
+    // unserialized request body as the N+1th method argument, where N is the
+    // number of route parameters specified on the accompanying route. To be
+    // able to use the argument resolver, which is not based on position but on
+    // name and typehint, specify commonly used names here. 0 or 1 of these will
+    // be used.
+    if (isset($unserialized)) {
+      $upcasted_route_arguments['entity'] = $unserialized;
+      $upcasted_route_arguments['data'] = $unserialized;
+      $upcasted_route_arguments['unserialized'] = $unserialized;
+    }
+
+    // In the case of GET the entity is stored in $upcasted_route_arguments in
+    // the $entity_type_id key, not as 'entity', which
+    // \Drupal\rest\Plugin\rest\resource\EntityResource::get() expects as their name.
+    if (!in_array($request->getMethod(), ['PATCH', 'POST'], TRUE) && empty($upcasted_route_arguments['entity'])) {
+      // Try to find a parameter which is an entity.
+      foreach ($route_arguments as $value) {
+        if ($value instanceof EntityInterface) {
+          $upcasted_route_arguments['entity'] = $value;
+        }
+      }
+    }
+
+    if (in_array($request->getMethod(), ['PATCH', 'POST'], TRUE) && empty($upcasted_route_arguments['original_entity'])) {
+      // Try to find a parameter which is an entity.
+      foreach ($route_arguments as $value) {
+        if ($value instanceof EntityInterface) {
+          $upcasted_route_arguments['original_entity'] = $value;
+        }
+      }
+    }
+
+    // Parameters which are not defined on the route object, but still are
+    // essential for access checking are passed as wildcards to the argument
+    // resolver.
+    $wildcard_arguments = [$route, $route_match];
+    if (isset($request)) {
+      $wildcard_arguments[] = $request;
+    }
+    if (isset($unserialized)) {
+      $wildcard_arguments[] = $unserialized;
+    }
+
+    return new ArgumentsResolver($raw_route_arguments, $upcasted_route_arguments, $wildcard_arguments);
+  }
+
+  /**
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The route match
+   * @param mixed $unserialized
+   *   The unserialized data.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
+   *
+   * @return array
+   */
+  protected function getLegacyParameters(RouteMatchInterface $route_match, $unserialized, Request $request) {
+    $route_parameters = $route_match->getParameters();
+    $parameters = [];
+    // Filter out all internal parameters starting with "_".
+    foreach ($route_parameters as $key => $parameter) {
+      if ($key{0} !== '_') {
+        $parameters[] = $parameter;
+      }
+    }
+
+    return array_merge($parameters, [$unserialized, $request]);
+  }
+
 }
