diff --git a/core/lib/Drupal/Core/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index 52af901..a9ebdc9 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -235,7 +235,9 @@ public function build(ContainerBuilder $container) {
     $container->register('serializer.normalizer.complex_data', 'Drupal\Core\Serialization\ComplexDataNormalizer')->addTag('normalizer');
     $container->register('serializer.normalizer.list', 'Drupal\Core\Serialization\ListNormalizer')->addTag('normalizer');
     $container->register('serializer.normalizer.typed_data', 'Drupal\Core\Serialization\TypedDataNormalizer')->addTag('normalizer');
-    $container->register('serializer.encoder.json', 'Drupal\Core\Serialization\JsonEncoder')->addTag('encoder');
+
+    $container->register('serializer.encoder.json', 'Drupal\Core\Serialization\JsonEncoder')
+      ->addTag('encoder', array('format' => array('json' => 'JSON')));
 
     $container->register('flood', 'Drupal\Core\Flood\DatabaseBackend')
       ->addArgument(new Reference('database'));
diff --git a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterSerializationClassesPass.php b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterSerializationClassesPass.php
index 6ed101b..bb5914d 100644
--- a/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterSerializationClassesPass.php
+++ b/core/lib/Drupal/Core/DependencyInjection/Compiler/RegisterSerializationClassesPass.php
@@ -42,6 +42,15 @@ public function process(ContainerBuilder $container) {
     if (!empty($encoders)) {
       $definition->replaceArgument(1, $this->sort($encoders));
     }
+
+    // Find all serialization formats known.
+    $formats = array();
+    foreach ($container->findTaggedServiceIds('encoder') as $id => $attributes) {
+      foreach ($attributes[0]['format'] as $name => $label) {
+        $formats[$name] = $label;
+      }
+    }
+    $container->setParameter('serializer.formats', $formats);
   }
 
   /**
diff --git a/core/modules/jsonld/lib/Drupal/jsonld/JsonldBundle.php b/core/modules/jsonld/lib/Drupal/jsonld/JsonldBundle.php
index 00f1433..176779a 100644
--- a/core/modules/jsonld/lib/Drupal/jsonld/JsonldBundle.php
+++ b/core/modules/jsonld/lib/Drupal/jsonld/JsonldBundle.php
@@ -63,7 +63,13 @@ public function build(ContainerBuilder $container) {
     // Add Encoders to service container.
     foreach ($encoders as $format => $encoder_class) {
       $container->register("serializer.encoder.{$format}", $encoder_class)
-        ->addTag('encoder', array('priority' => $priority));
+        ->addTag('encoder', array(
+          'priority' => $priority,
+          'format' => array(
+            'jsonld' => 'JSON-LD',
+            'drupal_jsonld' => 'Drupal JSON-LD',
+          ),
+        ));
     }
 
     $container->register('jsonld.subscriber', 'Drupal\jsonld\EventSubscriber\JsonldSubscriber')
diff --git a/core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php b/core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php
index 47e9420..726ba7c 100644
--- a/core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php
+++ b/core/modules/rest/lib/Drupal/rest/EventSubscriber/RouteSubscriber.php
@@ -54,16 +54,27 @@ public function __construct(ResourcePluginManager $manager, ConfigFactory $confi
   public function dynamicRoutes(RouteBuildEvent $event) {
 
     $collection = $event->getRouteCollection();
+    $enabled = $this->config->get('rest.settings')->load()->get('resources');
 
-    $resources = $this->config->get('rest.settings')->load()->get('resources');
-    if ($resources && $enabled = array_intersect_key($this->manager->getDefinitions(), $resources)) {
-      foreach ($enabled as $key => $resource) {
-        $plugin = $this->manager->getInstance(array('id' => $key));
+    // Iterate over all enabled resource plugins.
+    foreach ($enabled as $id => $enabled_methods) {
+      $plugin = $this->manager->getInstance(array('id' => $id));
 
-        // @todo Switch to ->addCollection() once http://drupal.org/node/1819018 is resolved.
-        foreach ($plugin->routes() as $name => $route) {
-          $route->setRequirement('_access', 'TRUE');
-          $collection->add("rest.$name", $route);
+      foreach ($plugin->routes() as $name => $route) {
+        $method = $route->getRequirement('_method');
+        // Only expose routes where the method is enabled in the configuration.
+        if ($method && isset($enabled_methods[$method])) {
+          // No format restrictions are configured, so always add the route.
+          if (empty($enabled_methods[$method])) {
+            $collection->add("rest.$name", $route);
+            continue;
+          }
+          // If there is no format requirement or if it matches the
+          // configuration also add the route.
+          $format_requirement = $route->getRequirement('_format');
+          if (!$format_requirement || isset($enabled_methods[$method][$format_requirement])) {
+            $collection->add("rest.$name", $route);
+          }
         }
       }
     }
diff --git a/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php b/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php
index f3292b7..187aea9 100644
--- a/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php
+++ b/core/modules/rest/lib/Drupal/rest/Plugin/ResourceBase.php
@@ -29,15 +29,11 @@
   public function permissions() {
     $permissions = array();
     $definition = $this->getDefinition();
-    foreach ($this->requestMethods() as $method) {
+    foreach ($this->availableMethods() as $method) {
       $lowered_method = strtolower($method);
-      // Only expose permissions where the HTTP request method exists on the
-      // plugin.
-      if (method_exists($this, $lowered_method)) {
-        $permissions["restful $lowered_method $this->plugin_id"] = array(
-          'title' => t('Access @method on %label resource', array('@method' => $method, '%label' => $definition['label'])),
-        );
-      }
+      $permissions["restful $lowered_method $this->plugin_id"] = array(
+        'title' => t('Access @method on %label resource', array('@method' => $method, '%label' => $definition['label'])),
+      );
     }
     return $permissions;
   }
@@ -56,38 +52,44 @@ public function routes() {
     $path_prefix = strtr($this->plugin_id, ':', '/');
     $route_name = strtr($this->plugin_id, ':', '.');
 
-    $methods = $this->requestMethods();
+    $methods = $this->availableMethods();
     foreach ($methods as $method) {
       $lower_method = strtolower($method);
-      // Only expose routes where the HTTP request method exists on the plugin.
-      if (method_exists($this, $lower_method)) {
-        $route = new Route("/$path_prefix/{id}", array(
-          '_controller' => 'Drupal\rest\RequestHandler::handle',
-          // Pass the resource plugin ID along as default property.
-          '_plugin' => $this->plugin_id,
-        ), array(
-          // The HTTP method is a requirement for this route.
-          '_method' => $method,
-          '_permission' => "restful $lower_method $this->plugin_id",
-        ));
+      $route = new Route("/$path_prefix/{id}", array(
+        '_controller' => 'Drupal\rest\RequestHandler::handle',
+        // Pass the resource plugin ID along as default property.
+        '_plugin' => $this->plugin_id,
+      ), array(
+        // The HTTP method is a requirement for this route.
+        '_method' => $method,
+        '_permission' => "restful $lower_method $this->plugin_id",
+      ));
 
-        switch ($method) {
-          case 'POST':
-            // POST routes do not require an ID in the URL path.
-            $route->setPattern("/$path_prefix");
-            $route->addDefaults(array('id' => NULL));
-            break;
+      switch ($method) {
+        case 'POST':
+          // POST routes do not require an ID in the URL path.
+          $route->setPattern("/$path_prefix");
+          $route->addDefaults(array('id' => NULL));
+          $collection->add("$route_name.$method", $route);
+          break;
 
-          case 'GET':
-          case 'HEAD':
-            // Restrict GET and HEAD requests to the media type specified in the
-            // HTTP Accept headers.
-            // @todo Replace hard coded format here with available formats.
-            $route->addRequirements(array('_format' => 'drupal_jsonld'));
-            break;
-        }
+        case 'GET':
+        case 'HEAD':
+          // Restrict GET and HEAD requests to the media type specified in the
+          // HTTP Accept headers.
+          $formats = drupal_container()->getParameter('serializer.formats');
+          foreach ($formats as $format_name => $label) {
+            // Expose one route per available format.
+            //$format_route = new Route($route->getPattern(), $route->getDefaults(), $route->getRequirements());
+            $format_route = clone $route;
+            $format_route->addRequirements(array('_format' => $format_name));
+            $collection->add("$route_name.$method.$format_name", $format_route);
+          }
+          break;
 
-        $collection->add("$route_name.$method", $route);
+        default:
+          $collection->add("$route_name.$method", $route);
+          break;
       }
     }
 
@@ -116,4 +118,22 @@ protected function requestMethods() {
       'PATCH',
     ));
   }
+
+  /**
+   * Returns available HTTP request methods on this plugin.
+   *
+   * @return array
+   *   Subset of requestMethods().
+   */
+  public function availableMethods() {
+    $methods = $this->requestMethods();
+    $available = array();
+    foreach ($methods as $method) {
+      // Only expose methods where the HTTP request method exists on the plugin.
+      if (method_exists($this, strtolower($method))) {
+        $available[] = $method;
+      }
+    }
+    return $available;
+  }
 }
diff --git a/core/modules/rest/lib/Drupal/rest/RequestHandler.php b/core/modules/rest/lib/Drupal/rest/RequestHandler.php
index b8d6d42..3484417 100644
--- a/core/modules/rest/lib/Drupal/rest/RequestHandler.php
+++ b/core/modules/rest/lib/Drupal/rest/RequestHandler.php
@@ -42,9 +42,8 @@ public function handle(Request $request, $id = NULL) {
     if (!empty($received)) {
       $definition = $resource->getDefinition();
       $class = $definition['serialization_class'];
-      // @todo Replace the format here with something we get from the HTTP
-      //   Content-type header. See http://drupal.org/node/1850704
-      $unserialized = $serializer->deserialize($received, $class, 'drupal_jsonld');
+      $format = $request->getContentType();
+      $unserialized = $serializer->deserialize($received, $class, $format);
     }
 
     // Invoke the operation on the resource plugin.
@@ -58,9 +57,15 @@ public function handle(Request $request, $id = NULL) {
     // Serialize the outgoing data for the response, if available.
     $data = $response->getResponseData();
     if ($data != NULL) {
-      // @todo Replace the format here with something we get from the HTTP
-      //   Accept headers. See http://drupal.org/node/1833440
-      $output = $serializer->serialize($data, 'drupal_jsonld');
+      // All REST routes are restricted to exactly one format, so instead of
+      // parsing it out of the Accept headers again we can simply retrieve the
+      // format requirement.
+      $format = $request->attributes->get('_route')->getRequirement('_format');
+      // If there is no format associated just pick Drupal JSON-LD.
+      if (!$format) {
+        $format = 'drupal_jsonld';
+      }
+      $output = $serializer->serialize($data, $format);
       $response->setContent($output);
       $response->headers->set('Content-Type', 'application/vnd.drupal.ld+json');
     }
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php b/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php
index b47f296..d25b776 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/CreateTest.php
@@ -38,7 +38,7 @@ public function testCreate() {
     // entity types here as well.
     $entity_type = 'entity_test';
 
-    $this->enableService('entity:' . $entity_type);
+    $this->enableService('entity:' . $entity_type, 'POST');
     // Create a user account that has the required permissions to create
     // resources via the web API.
     $account = $this->drupalCreateUser(array('restful post entity:' . $entity_type));
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php b/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php
index 66f85c4..cd61bab 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/DeleteTest.php
@@ -36,7 +36,7 @@ public function testDelete() {
     // Define the entity types we want to test.
     $entity_types = array('entity_test', 'node', 'user');
     foreach ($entity_types as $entity_type) {
-      $this->enableService('entity:' . $entity_type);
+      $this->enableService('entity:' . $entity_type, 'DELETE');
       // Create a user account that has the required permissions to delete
       // resources via the web API.
       $account = $this->drupalCreateUser(array('restful delete entity:' . $entity_type));
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php
index 3036977..84c3bde 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/RESTTestBase.php
@@ -158,18 +158,24 @@ protected function entityValues($entity_type) {
    * @param string|FALSE $resource_type
    *   The resource type that should get web API enabled or FALSE to disable all
    *   resource types.
+   * @param string $method
+   *   The HTTP method to enable, e.g. GET, POST etc.
+   * @param string $format
+   *   (Optional) The serialization format, e.g. jsonld.
    */
-  protected function enableService($resource_type) {
+  protected function enableService($resource_type, $method = 'GET', $format = NULL) {
     // Enable web API for this entity type.
     $config = config('rest.settings');
+    $settings = array();
     if ($resource_type) {
-      $config->set('resources', array(
-        $resource_type => $resource_type,
-      ));
-    }
-    else {
-      $config->set('resources', array());
+      if ($format) {
+        $settings[$resource_type][$method][$format] = 'TRUE';
+      }
+      else {
+        $settings[$resource_type][$method] = array();
+      }
     }
+    $config->set('resources', $settings);
     $config->save();
 
     // Rebuild routing cache, so that the web API paths are available.
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/ReadTest.php b/core/modules/rest/lib/Drupal/rest/Tests/ReadTest.php
index 6808020..2b765c1 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/ReadTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/ReadTest.php
@@ -38,7 +38,7 @@ public function testRead() {
     // Define the entity types we want to test.
     $entity_types = array('entity_test');
     foreach ($entity_types as $entity_type) {
-      $this->enableService('entity:' . $entity_type);
+      $this->enableService('entity:' . $entity_type, 'GET');
       // Create a user account that has the required permissions to delete
       // resources via the web API.
       $account = $this->drupalCreateUser(array('restful get entity:' . $entity_type));
diff --git a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
index 83729aa..669a265 100644
--- a/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
+++ b/core/modules/rest/lib/Drupal/rest/Tests/UpdateTest.php
@@ -38,7 +38,7 @@ public function testPatchUpdate() {
     // entity types here as well.
     $entity_type = 'entity_test';
 
-    $this->enableService('entity:' . $entity_type);
+    $this->enableService('entity:' . $entity_type, 'PATCH');
     // Create a user account that has the required permissions to create
     // resources via the web API.
     $account = $this->drupalCreateUser(array('restful patch entity:' . $entity_type));
@@ -106,7 +106,7 @@ public function testPutUpdate() {
     // entity types here as well.
     $entity_type = 'entity_test';
 
-    $this->enableService('entity:' . $entity_type);
+    $this->enableService('entity:' . $entity_type, 'PUT');
     // Create a user account that has the required permissions to create
     // resources via the web API.
     $account = $this->drupalCreateUser(array('restful put entity:' . $entity_type));
diff --git a/core/modules/rest/rest.admin.inc b/core/modules/rest/rest.admin.inc
index 8151cb8..6e0c1e5 100644
--- a/core/modules/rest/rest.admin.inc
+++ b/core/modules/rest/rest.admin.inc
@@ -11,49 +11,104 @@
  * @ingroup forms
  */
 function rest_admin_form($form, &$form_state) {
-  $resources = drupal_container()
-    ->get('plugin.manager.rest')
-    ->getDefinitions();
+  $manager = drupal_container()
+    ->get('plugin.manager.rest');
+  $resources = $manager->getDefinitions();
+  $formats = drupal_container()->getParameter('serializer.formats');
+  $enabled_resources = config('rest.settings')->get('resources') ?: array();
+
   $entity_resources = array();
   $other_resources = array();
   foreach ($resources as $plugin_name => $definition) {
     if (strpos($plugin_name, 'entity:') === FALSE) {
-      $other_resources[$plugin_name] = $definition['label'];
+      $other_resources[$plugin_name] = $definition;
     }
     else {
-      $entity_resources[$plugin_name] = $definition['label'];
+      $entity_resources[$plugin_name] = $definition;
     }
   }
-  asort($entity_resources);
-  asort($other_resources);
-  $enabled_resources = config('rest.settings')->get('resources') ?: array();
+
+  // Sort the available resource plugins per label.
+  $sorter = function ($a, $b) {
+    return strnatcasecmp($a['label'], $b['label']);
+  };
+  uasort($entity_resources, $sorter);
+  uasort($other_resources, $sorter);
 
   $form['entity_resources'] = array(
-    '#type' => 'checkboxes',
-    '#options' => $entity_resources,
-    '#default_value' => $enabled_resources,
+    '#type' => 'fieldset',
     '#title' => t('Entity resource types that should be exposed as web services:'),
-  );
+    '#tree' => TRUE,
+  ) + rest_admin_resource_fieldset('entity_resources', $entity_resources, $enabled_resources, $manager, $formats);
+
   if (!empty($other_resources)) {
     $form['other_resources'] = array(
-      '#type' => 'checkboxes',
-      '#options' => $other_resources,
-      '#default_value' => $enabled_resources,
+      '#type' => 'fieldset',
       '#title' => t('Other available resource types that should be exposed as web services:'),
-    );
+      '#tree' => TRUE,
+    ) + rest_admin_resource_fieldset('other_resources', $other_resources, $enabled_resources, $manager, $formats);
   }
 
   return system_config_form($form, $form_state);
 }
 
 /**
+ * Form building helper function for the nested configuration fieldsets.
+ */
+function rest_admin_resource_fieldset($category, $resources, $enabled_resources, $manager, $formats) {
+  $form = array();
+  foreach ($resources as $id => $definition) {
+    $resource = $manager->getInstance(array('id' => $id));
+    $form[$id]['enabled'] = array(
+      '#type' => 'checkbox',
+      '#title' => $definition['label'],
+      '#default_value' => isset($enabled_resources[$id]),
+    );
+    $form[$id]['operations'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Operations'),
+      '#description' => t('Select the operations that should be exposed.'),
+      '#states' => array(
+        'invisible' => array(
+          'input[name="' . $category . '[' . $id . '][enabled]"]' => array('checked' => FALSE),
+        ),
+      ),
+    );
+
+    foreach ($resource->availableMethods() as $name) {
+      $form[$id]['operations'][$name]['enabled'] = array(
+        '#type' => 'checkbox',
+        '#title' => $name,
+        '#default_value' => isset($enabled_resources[$id][$name]),
+      );
+      $form[$id]['operations'][$name]['formats'] = array(
+        '#type' => 'fieldset',
+        '#title' => t('Formats'),
+        '#description' => t('Optional: select requerst/response formats that should be allowed for the @operation operation.', array('@operation' => $name)),
+        '#states' => array(
+          'invisible' => array(
+            'input[name="' . $category . '[' . $id . '][operations][' . $name . '][enabled]"]' => array('checked' => FALSE),
+          ),
+        ),
+      );
+      foreach ($formats as $format_name => $format_label) {
+        $form[$id]['operations'][$name]['formats'][$format_name] = array(
+          '#type' => 'checkbox',
+          '#title' => $format_label,
+          '#default_value' => !empty($enabled_resources[$id][$name][$format_name]),
+        );
+      }
+    }
+  }
+  return $form;
+}
+
+/**
  * Form submission handler for rest_admin_form().
  */
 function rest_admin_form_submit($form, &$form_state) {
-  $resources = array_filter($form_state['values']['entity_resources']);
-  if (!empty($form_state['values']['other_resources'])) {
-    $resources += array_filter($form_state['values']['other_resources']);
-  }
+  $resources = rest_admin_extract_submission($form_state['values']['entity_resources'])
+    + rest_admin_extract_submission($form_state['values']['other_resources']);
 
   $config = config('rest.settings');
   $config->set('resources', $resources);
@@ -62,3 +117,25 @@ function rest_admin_form_submit($form, &$form_state) {
   // Rebuild routing cache.
   drupal_container()->get('router.builder')->rebuild();
 }
+
+/**
+ * Form submission helper to build the configuration array.
+ */
+function rest_admin_extract_submission($values) {
+  $resources = array();
+  foreach ($values as $plugin => $settings) {
+    if ($settings['enabled']) {
+      foreach ($settings['operations'] as $operation => $operation_settings) {
+        if ($operation_settings['enabled']) {
+          $resources[$plugin][$operation] = array();
+          foreach ($operation_settings['formats'] as $format => $enabled) {
+            if ($enabled) {
+              $resources[$plugin][$operation][$format] = 'TRUE';
+            }
+          }
+        }
+      }
+    }
+  }
+  return $resources;
+}
