diff --git a/restws.info b/restws.info
index 63d7a76..89512a7 100644
--- a/restws.info
+++ b/restws.info
@@ -3,5 +3,6 @@
 core = 7.x
 files[] = restws.entity.inc
 files[] = restws.formats.inc
+files[] = restws.negotiation.inc
 files[] = restws.test
 dependencies[] = entity
diff --git a/restws.module b/restws.module
index e078411..516bb37 100644
--- a/restws.module
+++ b/restws.module
@@ -6,6 +6,131 @@
  */
 
 /**
+ * Implements hook_init().
+ *
+ * As Drupal's page cache IDs are only determined by URL paths, and restws now
+ * uses the same URLs as regular Drupal pages, this could lead to poisoning
+ * the HTML page cache with content possibly in other formats like JSON.
+ * This check avoids the poisoning by disabling page caching if the request is
+ * to be handled by restws. Thanks to this check at hook_init(), consumers now
+ * can use regular URLs with an Accept header, even if page caching is enabled.
+ */
+function restws_init() {
+  if (empty(restws_format_requested()) && !empty(restws_type_requested())) {
+    $GLOBALS['conf']['cache_page'] = FALSE;
+  }
+}
+
+/**
+ * Returns the MIME type specified somehow in the request.
+ *
+ * This function is called in hook_init. The format must be either
+ * application/xml or application/json or application/rdf+xml.
+ *
+ * @return string|boolean The format string given in the HTTP Accept header, or
+ *   in a query parameter. The query parameter name is configurable through a
+ *   $conf['restws_param_name'] definition in settings.php. Default
+ *   name is restwsf. If nothing is found in either place, or something is
+ *   found but it's not application/(json|xml|rdf+xml), it returns FALSE.
+ */
+function restws_type_requested() {
+  // Get the format MIME type form the HTTP Accept header.
+  $mime_type_given = restws_type_requested_data();
+  if (!empty($mime_type_given)) {
+    $valid_formats = restws_restws_format_info();
+    $valid_mime_types = array();
+    foreach ($valid_formats as $format) {
+      $mime_type = $format['mime type'];
+      $valid_mime_types[] = $mime_type;
+    }
+    if (in_array($mime_type_given, $valid_mime_types)) {
+      // A proper format has been specified, return it.
+      return $mime_type_given;
+    }
+    else {
+      return FALSE;
+    }
+  }
+  elseif ($_SERVER['REQUEST_METHOD'] == 'DELETE' && empty($mime_type_given)) {
+    // We don't care about the format, just pick JSON.
+    return 'application/json';
+  }
+
+  return FALSE;
+}
+
+/**
+ * Returns the MIME type somehow given in the request.
+ *
+ * This aux function is called during hook_init.
+ *
+ * @return string|bool
+ *   The format string given in the
+ *   HTTP Accept header, or in the Content-Type header, whichever comes first.
+ *   If nothing is found in either place, it returns FALSE.
+ */
+function restws_type_requested_data() {
+  // Check the Accept HTTP header.
+  if (isset($_SERVER['HTTP_ACCEPT'])) {
+    $negotiator   = new RestWsFormatNegotiator();
+    $accept_header = $_SERVER['HTTP_ACCEPT'];
+    $priorities   = array('application/json', 'application/xml');
+    if (module_exists('rdf')) {
+      $priorities[] = 'application/rdf+xml';
+    }
+    $priorities[] = '*/*';
+    return $negotiator->getBest($accept_header, $priorities)->getValue();
+  }
+
+  // If no good Accept, check the Content-Type.
+  if (isset($_SERVER['CONTENT_TYPE'])) {
+    $parts = explode(',', $_SERVER['CONTENT_TYPE'], 2);
+    $content_type_given = $parts[0];
+    if (!empty($content_type_given)) {
+      return $content_type_given;
+    }
+  }
+
+  // Nothing found.
+  return FALSE;
+}
+
+/**
+ * Returns the entity operation based on the HTTP request method received.
+ *
+ * @param int $id
+ *   If given, operation returned is view instead of query.
+ *
+ * @return string
+ *   create, update, delete, view, or query.
+ */
+function restws_operation_requested($id) {
+  switch ($_SERVER['REQUEST_METHOD']) {
+    case 'POST':
+      $op = 'create';
+      break;
+
+    case 'PUT':
+      $op = 'update';
+      break;
+
+    case 'DELETE':
+      $op = 'delete';
+      break;
+
+    default:
+      if (!empty($id)) {
+        $op = 'view';
+      }
+      else {
+        $op = 'query';
+      }
+  }
+
+  return $op;
+}
+
+/**
  * Returns info about all defined resources.
  *
  * @param string $resource
@@ -149,8 +274,8 @@
         drupal_add_http_header('Content-Type', $format->mimeType());
       }
       catch (RestWSException $e) {
-        $message = check_plain($e->getHTTPError()) . ': ' . check_plain($e->getMessage());
-        $status_message = $e->getHTTPError();
+        $message = check_plain($e->getHttpError()) . ': ' . check_plain($e->getMessage());
+        $status_message = $e->getHttpError();
       }
     }
     else {
@@ -170,19 +295,30 @@
  */
 class RestWSException extends Exception {
 
-  public function getHTTPError() {
+  /**
+   * Short description for the error code.
+   *
+   * @return string
+   *   Short description for the error code.
+   */
+  public function getHttpError() {
     $code = $this->getCode();
     switch ($code) {
       case 403:
         return '403 Forbidden';
+
       case 404:
         return '404 Not Found';
+
       case 406:
         return '406 Not Acceptable';
+
       case 412:
         return '412 Precondition Failed';
+
       case 422:
         return '422 Unprocessable Entity';
+
       default:
         return '500 Internal Server Error';
     }
@@ -193,6 +329,14 @@
  * Implements hook_menu_alter().
  */
 function restws_menu_alter(&$items) {
+  restws_menu_alter_callbacks($items);
+  restws_menu_alter_query($items);
+}
+
+/**
+ * Aux funcion menu_alter part 1: inject restws callback as needed.
+ */
+function restws_menu_alter_callbacks(&$items) {
   foreach (restws_get_resource_info() as $resource => $info) {
     // Resource full path (e.g. /node/% or /user/%) for accessing specific
     // resources.
@@ -237,21 +381,29 @@
         'type' => MENU_CALLBACK,
       );
     }
+  }
+}
+
+/**
+ * Aux funcion menu_alter part 2: provide the entity/id/<format> feature.
+ */
+function restws_menu_alter_query(&$items) {
+  foreach (restws_get_resource_info() as $resource => $info) {
     // Querying menu paths.
     foreach (array_keys(restws_get_format_info()) as $format) {
       // Resource base path URLs with the suffixes (e.g. node.json or user.xml)
       // for querying.
-      if (isset($items["$menu_path.$format"])) {
+      if (isset($items["$menu_path/$format"])) {
         // Prepend the page callback and the resource to the page arguments.
-        if (!isset($items["$menu_path.$format"]['page arguments'])) {
-          $items["$menu_path.$format"]['page arguments'] = array();
+        if (!isset($items["$menu_path/$format"]['page arguments'])) {
+          $items["$menu_path/$format"]['page arguments'] = array();
         }
-        array_unshift($items["$menu_path.$format"]['page arguments'], $resource, $items["$menu_path.$format"]['page callback']);
-        $items["$menu_path.$format"]['page callback'] = 'restws_page_callback';
+        array_unshift($items["$menu_path/$format"]['page arguments'], $resource, $items["$menu_path/$format"]['page callback']);
+        $items["$menu_path/$format"]['page callback'] = 'restws_page_callback';
 
       }
       else {
-        $items["$menu_path.$format"] = array(
+        $items["$menu_path/$format"] = array(
           'page callback' => 'restws_page_callback',
           'page arguments' => array($resource),
           'access callback' => TRUE,
@@ -263,106 +415,138 @@
 }
 
 /**
+ * Get the id requested in the URI.
+ *
+ * @param string $resource
+ *   Entity type.
+ *
+ * @return unknown
+ *   Entity id the URI requested.
+ */
+function restws_id_requested($resource) {
+  $resource_info = restws_get_resource_info($resource);
+  $resource_pos = isset($resource_info['menu_path']) ? count(explode('/', $resource_info['menu_path'])) - 1 : 0;
+  $id = arg($resource_pos + 1);
+
+  return $id;
+}
+
+/**
+ * Get the format requested in the URI.
+ *
+ * @return string|bool
+ *   Format (json|xml|rdf) as requested in the URI.
+ */
+function restws_format_requested() {
+  $url_comp = explode('/', request_uri());
+  $format_req = end($url_comp);
+  $formats = restws_restws_format_info();
+  $valid_format_flag = FALSE;
+  foreach ($formats as $format => $info) {
+    if (strcasecmp($format_req, $format) == 0) {
+      $valid_format_flag = TRUE;
+      break;
+    }
+  }
+
+  // Now we have found a valid format, make sure beginning of the URI
+  // also matches a resource.
+  if ($valid_format_flag) {
+    foreach (restws_get_resource_info() as $resource => $info) {
+      // Resource full path (e.g. /node/ or /user/) for accessing specific
+      // resources.
+      $menu_path = isset($info['menu_path']) ? $info['menu_path'] . '/' : $resource . '/';
+      if (!empty($menu_path) && strpos(request_uri(), $menu_path) !== FALSE) {
+        // When resource matches the request URI, returns the format.
+        return $format_req;
+      }
+    }
+  }
+
+  return FALSE;
+}
+
+/**
  * Menu page callback.
  */
 function restws_page_callback($resource, $page_callback = NULL) {
-  // Determine the position of the resource and resource id in the path.
-  $resource_info = restws_get_resource_info($resource);
-  $resource_pos = isset($resource_info['menu_path']) ? count(explode('/', $resource_info['menu_path'])) - 1 : 0;
+  $file = variable_get('restws_debug_log');
+  $log = date(DATE_ISO8601) . "\n";
 
-  $id_arg = arg($resource_pos + 1);
-  $resource_arg = arg($resource_pos);
-  $format = FALSE;
-  $id = NULL;
-  // Check for an appended .format string on GET requests only to avoid CSRF
-  // attacks on POST requests.
-  if ($_SERVER['REQUEST_METHOD'] == 'GET' && ($pos = strrpos($id_arg, '.')) && $format_name = substr($id_arg, $pos + 1)) {
-    $id = substr($id_arg, 0, $pos);
-    $format = restws_format($format_name);
+  if ($file) {
+    $log .= 'Resource: ' . $resource . "\n";
   }
-  elseif ($_SERVER['REQUEST_METHOD'] == 'GET' && ($pos = strrpos($resource_arg, '.')) && $format_name = substr($resource_arg, $pos + 1)) {
-    $format = restws_format($format_name);
+
+  // CSRF protection on write operations.
+  if (!in_array($_SERVER['REQUEST_METHOD'], array(
+    'GET',
+    'HEAD',
+    'OPTIONS',
+    'TRACE',
+  )) && !restws_csrf_validation()) {
+    restws_terminate_request('403 Forbidden', '403 Access Denied: CSRF validation failed');
+  }
+
+  // Determine the position of the resource and resource id in the path.
+  $id = restws_id_requested($resource);
+  if ($file) {
+    $log .= 'Id: ' . $id . "\n";
+  }
+
+  // Check if there is a format specified in the URI, like node/1/json
+  $format_req = restws_format_requested();
+  if ($file) {
+    $log .= 'Format requested via URI: ' . $format_req . "\n";
+  }
+
+  if ($format_req === FALSE) {
+    // Check for a MIME type requested in the Accept or Content-Type header.
+    $type = restws_type_requested();
+    if ($file) {
+      $log .= 'MIME Type requested: ' . $type . "\n";
+    }
+
+    if ($type) {
+      // Assigns the format from the Accept or Content-Type header.
+      $format = restws_format_mimetype($type);
+    }
+    // Or else $format goes on empty.
   }
   else {
-    $id = $id_arg;
-    switch ($_SERVER['REQUEST_METHOD']) {
-      case 'POST':
-      case 'PUT':
-        // Get format MIME type form HTTP Content type header.
-        $parts = explode(';', $_SERVER['CONTENT_TYPE'], 2);
-        $format = restws_format_mimetype($parts[0]);
-        break;
-
-      case 'DELETE':
-        if (isset($_SERVER['HTTP_ACCEPT'])) {
-          $parts = explode(',', $_SERVER['HTTP_ACCEPT'], 2);
-          $format = restws_format_mimetype($parts[0]);
-        }
-        if (!$format) {
-          // We don't care about the format, just pick JSON.
-          $format = restws_format('json');
-        }
-        break;
-
-      default:
-        // Get the format MIME type form the HTTP Accept header.
-        // Ignore requests from web browsers that accept HTML.
-        if (isset($_SERVER['HTTP_ACCEPT']) && strpos($_SERVER['HTTP_ACCEPT'], 'html') === FALSE) {
-          // Use the first MIME type.
-          $parts = explode(',', $_SERVER['HTTP_ACCEPT'], 2);
-          $format = restws_format_mimetype($parts[0]);
-        }
-        // Consumers should not use this URL if page caching is enabled.
-        // Drupal's page cache IDs are only determined by URL path, so this
-        // could poison the HTML page cache. A browser request to /node/1 could
-        // suddenly return JSON if the cache was primed with this RESTWS
-        // response.
-        if ($format && !isset($_COOKIE[session_name()]) && variable_get('cache')) {
-          // Redirect to the URL path containing the format name instead.
-          drupal_goto($_GET['q'] . '.' . $format->getName(), array(), 301);
-        }
-    }
+    // Assignes the format from the URI.
+    $format = restws_format($format_req);
   }
-  if ($format) {
-    switch ($_SERVER['REQUEST_METHOD']) {
-      case 'POST':
-        $op = 'create';
-        break;
 
-      case 'PUT':
-        $op = 'update';
-        break;
-
-      case 'DELETE':
-        $op = 'delete';
-        break;
-
-      default:
-        if (!empty($id)) {
-          $op = 'view';
-        }
-        else {
-          $op  = 'query';
-        }
-    }
-
-    // CSRF protection on write operations.
-    if (!in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD', 'OPTIONS', 'TRACE')) && !restws_csrf_validation()) {
-      restws_terminate_request('403 Forbidden', '403 Access Denied: CSRF validation failed');
-    }
-
-    $payload = file_get_contents('php://input');
-    if ($file = variable_get('restws_debug_log')) {
-      $log = date(DATE_ISO8601) . "\n";
-      $log .= 'Resource: ' . $resource . "\n";
-      $log .= 'Operation: ' . $op . "\n";
+  if (!empty($format)) {
+    if ($file) {
       $log .= 'Format: ' . $format->mimeType() . "\n";
-      $log .= 'Id: ' . $id . "\n";
+    }
+    // Determine CRUD operation from the HTTP method in the request.
+    $op = restws_operation_requested($id);
+    if ($file) {
+      $log .= 'Operation: ' . $op . "\n";
+    }
+
+    // Read the payload in the body of the request.
+    $payload = file_get_contents('php://input');
+    if ($file) {
       $log .= 'Payload: ' . $payload . "\n";
+    }
+
+    // Log tracing information if logging is enabled.
+    if ($file) {
       $log .= "----------------------------------------------------------------\n";
       file_put_contents($file, $log, FILE_APPEND);
     }
+
     restws_handle_request($op, $format, $resource, $id, $payload);
+  }
+
+  // Log tracing information even if format is empty.
+  if ($file) {
+    $log .= "Format: <empty>\n";
+    $log .= "----------------------------------------------------------------\n";
+    file_put_contents($file, $log, FILE_APPEND);
   }
 
   // @todo: Determine human readable URIs and redirect, if there is no
@@ -496,10 +680,10 @@
 /**
  * Terminates the current request with the given status code and message.
  *
- * @param $status_message
+ * @param string $status_message
  *   (optional) The status message (including the status code) to return via the
  *   HTTP Status header.
- * @param $message
+ * @param string $message
  *   (optional) A message displayed as body of the response.
  */
 function restws_terminate_request($status_message = NULL, $message = NULL) {
diff --git a/restws.test b/restws.test
index 06387a1..6fd7ef3 100644
--- a/restws.test
+++ b/restws.test
@@ -28,7 +28,7 @@
     $node = $this->drupalCreateNode(array('title' => $title));
     $account = $this->drupalCreateUser(array('access resource node'));
     $this->drupalLogin($account);
-    $result = $this->httpRequest('node/' . $node->nid . '.json', 'GET', $account);
+    $result = $this->httpRequest('node/' . $node->nid . '/json', 'GET', $account);
     $node_array = drupal_json_decode($result);
     $this->assertEqual($node->title, $node_array['title'], 'Node title was received correctly.');
     $this->assertResponse('200', 'HTTP response code is correct.');
@@ -369,7 +369,7 @@
     unset($this->curlHandle);
     // Request the JSON representation of the node.
     $this->drupalGet("node/$node->nid", array(), array('Accept: application/json'));
-    $this->assertUrl("node/$node->nid.json", array(), 'Requesting a resource with JSON Accept header redirects to the .json URL.');
+    $this->assertEqual($content_type, 'application/json', 'Content type header is application/json after requesting a regular node URL with Accept=application/json.');
     // Now request the HTML representation.
     $result = $this->drupalGet("node/$node->nid");
     $content_type = $this->drupalGetHeader('content-type');