diff --git a/core/modules/rest/src/Plugin/ResourceBase.php b/core/modules/rest/src/Plugin/ResourceBase.php
index b40a03109b..e91afd9da0 100644
--- a/core/modules/rest/src/Plugin/ResourceBase.php
+++ b/core/modules/rest/src/Plugin/ResourceBase.php
@@ -130,6 +130,10 @@ public function routes() {
             // Expose one route per available format.
             $format_route = clone $route;
             $format_route->addRequirements(['_format' => $format_name]);
+
+            $methods = $route->getMethods();
+            $methods[] = 'POST';
+            $route->setMethods($methods);
             $collection->add("$route_name.$method.$format_name", $format_route);
           }
           break;
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 5d9849ded4..68abeea830 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -18,9 +18,11 @@
 use Psr\Log\LoggerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\rest\ModifiedResourceResponse;
+use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
+use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
 use Symfony\Component\HttpKernel\Exception\HttpException;
 
 /**
@@ -114,12 +116,19 @@ public static function create(ContainerInterface $container, array $configuratio
    * @param \Drupal\Core\Entity\EntityInterface $entity
    *   The entity object.
    *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The HTTP request.
+   *
    * @return \Drupal\rest\ResourceResponse
    *   The response containing the entity with its accessible fields.
    *
    * @throws \Symfony\Component\HttpKernel\Exception\HttpException
    */
-  public function get(EntityInterface $entity) {
+  public function get(EntityInterface $entity, Request $request) {
+    if ($request->getMethod() === 'POST') {
+      throw new ConflictHttpException('POSTING to an already existing entity resource');
+    }
+
     $entity_access = $entity->access('view', NULL, TRUE);
     if (!$entity_access->isAllowed()) {
       throw new AccessDeniedHttpException($entity_access->getReason() ?: $this->generateFallbackAccessDeniedMessage($entity, 'view'));
diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php
index e5437ccb68..aea0509fea 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,91 @@ 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;
+
+    // For request methods that have request bodies, ResourceInterface plugin
+    // methods 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. Similarly, those methods receive the original stored object
+    // as the first method argument.
+    if (in_array($request->getMethod(), ['PATCH', 'POST'], TRUE)) {
+      $upcasted_route_arguments['entity'] = $unserialized;
+      $upcasted_route_arguments['data'] = $unserialized;
+      $upcasted_route_arguments['unserialized'] = $unserialized;
+
+      // Try to find a parameter which is an entity.
+      foreach ($route_arguments as $value) {
+        if ($value instanceof EntityInterface) {
+          $upcasted_route_arguments['original_entity'] = $value;
+        }
+      }
+    }
+    else {
+      // Try to find a parameter which is an entity.
+      foreach ($route_arguments as $value) {
+        if ($value instanceof EntityInterface) {
+          $upcasted_route_arguments['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]);
+  }
+
 }
diff --git a/core/modules/rest/tests/modules/rest_test/src/Plugin/rest/resource/NoSerializationClassTestResource.php b/core/modules/rest/tests/modules/rest_test/src/Plugin/rest/resource/NoSerializationClassTestResource.php
index 3e83a4ce74..2689588481 100644
--- a/core/modules/rest/tests/modules/rest_test/src/Plugin/rest/resource/NoSerializationClassTestResource.php
+++ b/core/modules/rest/tests/modules/rest_test/src/Plugin/rest/resource/NoSerializationClassTestResource.php
@@ -25,7 +25,7 @@ class NoSerializationClassTestResource extends ResourceBase {
    *
    * @return \Drupal\rest\ResourceResponse
    */
-  public function post(array $data = []) {
+  public function post(array $data) {
     return new ResourceResponse($data);
   }
 
diff --git a/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php b/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php
index 2b8855917e..808d434cc0 100644
--- a/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php
+++ b/core/modules/rest/tests/src/Kernel/RequestHandlerTest.php
@@ -93,9 +93,9 @@ public function testHandle() {
  */
 class StubRequestHandlerResourcePlugin extends ResourceBase {
 
-  public function get() {}
+  public function get($example, Request $request) {}
   public function post() {}
-  public function patch() {}
+  public function patch($example_original, Request $request) {}
   public function delete() {}
 
 }
