ruby-on-railsapirespond-with

Rails 3: respond_with and Completed 406 Not Acceptable


I am building an API for a Rails app. I actually have two apps: a server (the API) and a client (interacting with the API). I am trying to do a post request to create a model object via an API call. When the object is valid, the model object is created but I get a Completed 406 Not Acceptable error. The server throws the error silently, so a response object is returned. Enough writing/talking, here is some code:

Server API controller

class Api::V1::RequestsController < Api::V1::BaseController
  respond_to :json

  def create
    respond_with Request.create!(params[:request])
  end
end

Server routes

namespace :api do
  scope module: :v1 do
    resources :requests, only: [:index, :create]
  end
end

Client controller

class RequestsController < ApplicationController
  def new
    @request = Request.new
  end

  def create
    @request = Request.new(params[:request])
    if response = @request.save
      flash[:success] = "Request successfully sent"

      p response

      redirect_to request_url(@request)
    else
      render :new
    end
  end
end

Client model

class Request < ActiveRecord::Base
  include HTTParty

  if Rails.env.development?
    base_uri "http://localhost:3000/"
  end

  def save
    if self.valid?
      self.class.post("/api/requests", { body: {
        request: {
          artist_name: self.artist_name,
          email: self.email,
          phone_number: self.phone_number,
          country: self.country,
          city: self.city
        }
      } })
    else
      false
    end
  end
end

Server logs

Started POST "/api/requests" for 127.0.0.1 at 2013-01-27 17:32:35 +0000
Processing by Api::V1::RequestsController#create as HTML
  Parameters: {"request"=>{"artist_name"=>"Knife Party", "email"=>"admin@example.com", "phone_number"=>"07949967627", "country"=>"France", "city"=>"Paris"}}
   (0.4ms)  BEGIN
  Request Exists (0.4ms)  SELECT 1 AS one FROM "requests" WHERE LOWER("requests"."email") = LOWER('admin@example.com') LIMIT 1
  SQL (1.0ms)  INSERT INTO "requests" ("artist_name", "city", "country", "created_at", "email", "phone_number", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"  [["artist_name", "Knife Party"], ["city", "Paris"], ["country", "France"], ["created_at", Sun, 27 Jan 2013 17:32:35 UTC +00:00], ["email", "admin@example.com"], ["phone_number", "07949967627"], ["updated_at", Sun, 27 Jan 2013 17:32:35 UTC +00:00]]
   (0.3ms)  COMMIT
Completed 406 Not Acceptable in 27ms (ActiveRecord: 2.1ms)

Client logs (with the response object from the server)

#<HTTParty::Response:0x7f836dd944f8 parsed_response="         <!-- Footnotes Style -->\n        <style type=\"text/css\">\n          #footnotes_debug {font-size: 11px; font-weight: normal; margin: 2em 0 1em 0; text-align: center; color: #444; line-height: 16px;}\n          #footnotes_debug th, #footnotes_debug td {color: #444; line-height: 18px;}\n          #footnotes_debug a {color: #9b1b1b; font-weight: inherit; text-decoration: none; line-height: 18px;}\n          #footnotes_debug table {text-align: center;}\n          #footnotes_debug table td {padding: 0 5px;}\n          #footnotes_debug tbody {text-align: left;}\n          #footnotes_debug .name_values td {vertical-align: top;}\n          #footnotes_debug legend {background-color: #fff;}\n          #footnotes_debug fieldset {text-align: left; border: 1px dashed #aaa; padding: 0.5em 1em 1em 1em; margin: 1em 2em; color: #444; background-color: #FFF;}\n          /* Aditional Stylesheets */\n          \n        </style>\n        <!-- End Footnotes Style -->\n        <!-- Footnotes -->\n        <div style=\"clear:both\"></div>\n        <div id=\"footnotes_debug\">\n          Edit: <a href=\"txmt://open?url=file:///Users/aziz/Sandbox/ruby/rails/Azoui/warden/app/controllers/api/v1/requests_controller.rb&amp;line=8&amp;column=3\" onclick=\"\">Controller</a> | \n<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('partials_debug_info');return false;\">Partials (0)</a> | \n<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('stylesheets_debug_info');return false;\">Stylesheets (0)</a> | \n<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('javascripts_debug_info');return false;\">Javascripts (0)</a><br />Show: <a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('session_debug_info');return false;\">Session (0)</a> | \n<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('cookies_debug_info');return false;\">Cookies (0)</a> | \n<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('params_debug_info');return false;\">Params (3)</a> | \n<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('filters_debug_info');return false;\">Filters</a> | \n<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('routes_debug_info');return false;\">Routes</a> | \n<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('env_debug_info');return false;\">Env</a> | \n<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('queries_debug_info');return false;\">        <span style=\"background-color:#ffffff\">Queries (4)</span>\n        <span style=\"background-color:#ffffff\">DB (2.011ms)</span>\n</a> | \n<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('log_debug_info');return false;\">Log (0)</a> | \n<a href=\"#\" onclick=\"Footnotes.hideAllAndToggle('general_debug_info');return false;\">General Debug</a><br />\n                      <fieldset id=\"partials_debug_info\" style=\"display: none\">\n              <legend>Partials</legend>\n              <div></div>\n            </fieldset>\n            <fieldset id=\"stylesheets_debug_info\" style=\"display: none\">\n              <legend>Stylesheets</legend>\n              <div></div>\n            </fieldset>\n            <fieldset id=\"javascripts_debug_info\" style=\"display: none\">\n              <legend>Javascripts</legend>\n              <div></div>\n            </fieldset>\n            <fieldset id=\"session_debug_info\" style=\"display: none\">\n              <legend>Session</legend>\n              <div></div>\n            </fieldset>\n            <fieldset id=\"cookies_debug_info\" style=\"display: none\">\n              <legend>Cookies</legend>\n              <div></div>\n            </fieldset>\n            <fieldset id=\"params_debug_info\" style=\"display: none\">\n              <legend>Params</legend>\n              <div>          <table class=\"name_value\" summary=\"Debug information for Params (3)\" >\n            <thead><tr><th>Name</th><th>Value</th></tr></thead>\n            <tbody><tr><td>:request</td><td>{\"artist_name\"=&gt;\"Knife Party\", \"email\"=&gt;\"person@example.com\", \"phone_number\"=&gt;\"07949967627\", \"country\"=&gt;\"France\", \"city\"=&gt;\"Paris\"}</td></tr><tr><td>:action</td><td>\"create\"</td></tr><tr><td>:controller</td><td>\"api/v1/requests\"</td></tr></tbody>\n          </table>\n</div>\n            </fieldset>\n            <fieldset id=\"filters_debug_info\" style=\"display: none\">\n              <legend>Filter chain for Api::V1::RequestsController</legend>\n              <div></div>\n            </fieldset>\n            <fieldset id=\"routes_debug_info\" style=\"display: none\">\n              <legend>Routes for Api::V1::RequestsController</legend>\n              <div>          <table summary=\"Debug information for Routes\" >\n            <thead><tr><th>Path</th><th>Name</th><th>Options</th><th>Requirements</th></tr></thead>\n            <tbody><tr><td>api_requests</td><td>request_method</td><td>{:action=>\"index\"}</td><td>{:request_method=>/^GET$/}</td></tr><tr><td></td><td>request_method</td><td>{:action=>\"create\"}</td><td>{:request_method=>/^POST$/}</td></tr></tbody>\n          </table>\n</div>\n            </fieldset>\n            <fieldset id=\"env_debug_info\" style=\"display: none\">\n              <legend>Env</legend>\n              <div>          <table >\n            <thead><tr><th>Key</th><th>Value</th></tr></thead>\n            <tbody><tr><td>CONTENT_LENGTH</td><td>148</td></tr><tr><td>CONTENT_TYPE</td><td>application/x-www-form-urlencoded</td></tr><tr><td>GATEWAY_INTERFACE</td><td>CGI/1.2</td></tr><tr><td>HTTP_CONNECTION</td><td>close</td></tr><tr><td>HTTP_HOST</td><td>localhost:3000</td></tr><tr><td>HTTP_VERSION</td><td>HTTP/1.1</td></tr><tr><td>ORIGINAL_FULLPATH</td><td>/api/requests</td></tr><tr><td>PATH_INFO</td><td>/api/requests</td></tr><tr><td>QUERY_STRING</td><td></td></tr><tr><td>REMOTE_ADDR</td><td>127.0.0.1</td></tr><tr><td>REQUEST_METHOD</td><td>POST</td></tr><tr><td>REQUEST_PATH</td><td>/api/requests</td></tr><tr><td>REQUEST_URI</td><td>/api/requests</td></tr><tr><td>SCRIPT_NAME</td><td></td></tr><tr><td>SERVER_NAME</td><td>localhost</td></tr><tr><td>SERVER_PORT</td><td>3000</td></tr><tr><td>SERVER_PROTOCOL</td><td>HTTP/1.1</td></tr><tr><td>SERVER_SOFTWARE</td><td>thin 1.5.0 codename Knife</td></tr><tr><td>action_controller.instance</td><td>#&lt;Api::V1::RequestsController:0x007f84c7688618&gt;</td></tr><tr><td>action_dispatch.backtrace_cleaner</td><td>#&lt;Rails::BacktraceCleaner:0x007f84c6f8d8e0&gt;</td></tr><tr><td>action_dispatch.cookies</td><td>#&lt;ActionDispatch::Cookies::CookieJar:0x007f84c7686de0&gt;</td></tr><tr><td>action_dispatch.logger</td><td>#&lt;ActiveSupport::TaggedLogging:0x007f84c6e51468&gt;</td></tr><tr><td>action_dispatch.parameter_filter</td><td>[:password]</td></tr><tr><td>action_dispatch.remote_ip</td><td>127.0.0.1</td></tr><tr><td>action_dispatch.request.content_type</td><td>application/x-www-form-urlencoded</td></tr><tr><td>action_dispatch.request.formats</td><td>[text/html]</td></tr><tr><td>action_dispatch.request.parameters</td><td>{\"request\"=&gt;{\"artist_name\"=&gt;\"Knife Party\", \"email\"=&gt;\"person@example.com\", \"phone_number\"=&gt;\"07949967627\", \"country\"=&gt;\"France\", \"city\"=&gt;\"Paris\"}, \"action\"=&gt;\"create\", \"controller\"=&gt;\"api/v1/requests\"}</td></tr><tr><td>action_dispatch.request.path_parameters</td><td>{:action=&gt;\"create\", :controller=&gt;\"api/v1/requests\"}</td></tr><tr><td>action_dispatch.request.query_parameters</td><td>{}</td></tr><tr><td>action_dispatch.request.request_parameters</td><td>{\"request\"=&gt;{\"artist_name\"=&gt;\"Knife Party\", \"email\"=&gt;\"person@example.com\", \"phone_number\"=&gt;\"07949967627\", \"country\"=&gt;\"France\", \"city\"=&gt;\"Paris\"}}</td></tr><tr><td>action_dispatch.request.unsigned_session_cookie</td><td>{}</td></tr><tr><td>action_dispatch.request_id</td><td>ca38df31ac3758fbed0753f83efe7bf8</td></tr><tr><td>action_dispatch.routes</td><td>#&lt;ActionDispatch::Routing::RouteSet:0x007f84c6d6dc18&gt;</td></tr><tr><td>action_dispatch.secret_token</td><td>184d9143ce94750263caefe09316bcff11b84d5f32f51e6628665f2a7fe946828044aec5f5ce72780c6e3ccf85e26a2b50f306990968e1c44634cae42cc90f1d</td></tr><tr><td>action_dispatch.show_detailed_exceptions</td><td>true</td></tr><tr><td>action_dispatch.show_exceptions</td><td>true</td></tr><tr><td>async.callback</td><td>#&lt;Method: Thin::Connection#post_process&gt;</td></tr><tr><td>async.close</td><td>#&lt;EventMachine::DefaultDeferrable:0x007f84c68daf98&gt;</td></tr><tr><td>rack.errors</td><td>#&lt;IO:0x007f84c285a450&gt;</td></tr><tr><td>rack.input</td><td>#&lt;StringIO:0x007f84c7716a08&gt;</td></tr><tr><td>rack.multiprocess</td><td>false</td></tr><tr><td>rack.multithread</td><td>false</td></tr><tr><td>rack.request.cookie_hash</td><td>{}</td></tr><tr><td>rack.request.form_hash</td><td>{\"request\"=&gt;{\"artist_name\"=&gt;\"Knife Party\", \"email\"=&gt;\"person@example.com\", \"phone_number\"=&gt;\"07949967627\", \"country\"=&gt;\"France\", \"city\"=&gt;\"Paris\"}}</td></tr><tr><td>rack.request.form_input</td><td>#&lt;StringIO:0x007f84c7716a08&gt;</td></tr><tr><td>rack.request.form_vars</td><td>request[artist_name]=Knife%20Party&amp;request[email]=person%40example.com&amp;request[phone_number]=07949967627&amp;request[country]=France&amp;request[city]=Paris</td></tr><tr><td>rack.request.query_hash</td><td>{}</td></tr><tr><td>rack.request.query_string</td><td></td></tr><tr><td>rack.run_once</td><td>false</td></tr><tr><td>rack.session</td><td>{}</td></tr><tr><td>rack.session.options</td><td>{:path=&gt;\"/\", :domain=&gt;nil, :expire_after=&gt;nil, :secure=&gt;false, :httponly=&gt;true, :defer=&gt;false, :renew=&gt;false, :secret=&gt;\"98d3f4e78a03eb75aa2bc26d3d26c10524c72dc70035198d8fc73d99aef3\", :coder=&gt;#&lt;Rack::Session::Cookie::Base64::Marshal:0x007f84c75e6548&gt;, :id=&gt;nil}</td></tr><tr><td>rack.url_scheme</td><td>http</td></tr><tr><td>rack.version</td><td>[1, 0]</td></tr></tbody>\n          </table>\n</div>\n            </fieldset>\n            <fieldset id=\"queries_debug_info\" style=\"display: none\">\n              <legend>Queries</legend>\n              <div>            <b id=\"qtitle_0\">UNKNOWN</b> (<a href=\"javascript:Footnotes.toggle('qtrace_0')\" style=\"color:#00A;\">trace</a>)<br />\n            <span id=\"sql_0\">BEGIN</span><br />\n            <span style='background-color:#ffffff'>SQL (0.000ms)</span>&nbsp;\n            <p id=\"qtrace_0\" style=\"display:none;\"><a href=\"txmt://open?url=file:///Users/aziz/Sandbox/ruby/rails/Azoui/warden/app/controllers/api/v1/requests_controller.rb&amp;amp;line=9&amp;amp;column=1\">app/controllers/api/v1/requests_controller.rb:9:in `create'</a><br /></p><br />\n            <b id=\"qtitle_1\">SELECT</b> (<a href=\"javascript:Footnotes.toggle('qtrace_1')\" style=\"color:#00A;\">trace</a>)<br />\n            <span id=\"sql_1\">SELECT 1 AS one FROM \"requests\" WHERE LOWER(\"requests\".\"email\") = LOWER('person@example.com') LIMIT 1</span><br />\n            <span style='background-color:#ffffff'>Request Exists (0.001ms)</span>&nbsp;\n            <p id=\"qtrace_1\" style=\"display:none;\"><a href=\"txmt://open?url=file:///Users/aziz/Sandbox/ruby/rails/Azoui/warden/app/controllers/api/v1/requests_controller.rb&amp;amp;line=9&amp;amp;column=1\">app/controllers/api/v1/requests_controller.rb:9:in `create'</a><br /></p><br />\n            <b id=\"qtitle_2\">INSERT</b> (<a href=\"javascript:Footnotes.toggle('qtrace_2')\" style=\"color:#00A;\">trace</a>)<br />\n            <span id=\"sql_2\">INSERT INTO \"requests\" (\"artist_name\", \"city\", \"country\", \"created_at\", \"email\", \"phone_number\", \"updated_at\") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING \"id\"</span><br />\n            <span style='background-color:#ffffff'>SQL (0.001ms)</span>&nbsp;\n            <p id=\"qtrace_2\" style=\"display:none;\"><a href=\"txmt://open?url=file:///Users/aziz/Sandbox/ruby/rails/Azoui/warden/app/controllers/api/v1/requests_controller.rb&amp;amp;line=9&amp;amp;column=1\">app/controllers/api/v1/requests_controller.rb:9:in `create'</a><br /></p><br />\n            <b id=\"qtitle_3\">UNKNOWN</b> (<a href=\"javascript:Footnotes.toggle('qtrace_3')\" style=\"color:#00A;\">trace</a>)<br />\n            <span id=\"sql_3\">COMMIT</span><br />\n            <span style='background-color:#ffffff'>SQL (0.000ms)</span>&nbsp;\n            <p id=\"qtrace_3\" style=\"display:none;\"><a href=\"txmt://open?url=file:///Users/aziz/Sandbox/ruby/rails/Azoui/warden/app/controllers/api/v1/requests_controller.rb&amp;amp;line=9&amp;amp;column=1\">app/controllers/api/v1/requests_controller.rb:9:in `create'</a><br /></p><br />\n</div>\n            </fieldset>\n            <fieldset id=\"log_debug_info\" style=\"display: none\">\n              <legend>Log</legend>\n              <div></div>\n            </fieldset>\n            <fieldset id=\"general_debug_info\" style=\"display: none\">\n              <legend>General (id=\"general_debug_info\")</legend>\n              <div>You can use this tab to debug other parts of your application, for example Javascript.</div>\n            </fieldset>\n\n          <script type=\"text/javascript\">\n            var Footnotes = function() {\n\n              function hideAll(){\n                Footnotes.hide(document.getElementById('partials_debug_info'));\nFootnotes.hide(document.getElementById('stylesheets_debug_info'));\nFootnotes.hide(document.getElementById('javascripts_debug_info'));\nFootnotes.hide(document.getElementById('session_debug_info'));\nFootnotes.hide(document.getElementById('cookies_debug_info'));\nFootnotes.hide(document.getElementById('params_debug_info'));\nFootnotes.hide(document.getElementById('filters_debug_info'));\nFootnotes.hide(document.getElementById('routes_debug_info'));\nFootnotes.hide(document.getElementById('env_debug_info'));\nFootnotes.hide(document.getElementById('queries_debug_info'));\nFootnotes.hide(document.getElementById('log_debug_info'));\nFootnotes.hide(document.getElementById('general_debug_info'));\n\n              }\n\n              function hideAllAndToggle(id) {\n                hideAll();\n                toggle(id)\n\n                location.href = '#footnotes_debug';\n              }\n\n              function toggle(id){\n                var el = document.getElementById(id);\n                if (el.style.display == 'none') {\n                  Footnotes.show(el);\n                } else {\n                  Footnotes.hide(el);\n                }\n              }\n\n              function show(element) {\n                element.style.display = 'block'\n              }\n\n              function hide(element) {\n                element.style.display = 'none'\n              }\n\n              return {\n                show: show,\n                hide: hide,\n                toggle: toggle,\n                hideAllAndToggle: hideAllAndToggle\n              }\n            }();\n            /* Additional Javascript */\n            \n          </script>\n        </div>\n        <!-- End Footnotes -->\n", @response=#<Net::HTTPNotAcceptable 406 Not Acceptable readbody=true>, @headers={"content-type"=>["text/html; charset=utf-8"], "x-ua-compatible"=>["IE=Edge"], "cache-control"=>["no-cache"], "x-request-id"=>["ca38df31ac3758fbed0753f83efe7bf8"], "x-runtime"=>["0.027491"], "connection"=>["close"], "server"=>["thin 1.5.0 codename Knife"]}>


Started POST "/requests" for 127.0.0.1 at 2013-01-27 17:34:07 +0000
Processing by RequestsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"Eat6tObc84Oi+D4hjjoYZermjxi0+QVtEb9FRVJOxnU=", "request"=>{"artist_name"=>"Knife Party", "email"=>"person@example.com", "phone_number"=>"07949967627", "country"=>"France", "city"=>"Paris"}, "commit"=>"Send request"}
Completed 404 Not Found in 34ms

ActionController::RoutingError (No route matches {:action=>"show", :controller=>"requests", :id=>#<Request artist_name: "Knife Party", email: "person@example.com", phone_number: "07949967627", country: "France", city: "Paris">}):
  app/controllers/requests_controller.rb:20:in `create'


  Rendered /Users/aziz/.rvm/gems/ruby-1.9.3-p327@portal/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/routing_error.erb within rescues/layout (0.8ms)

Client gems used (and versions)

gem 'rails', '3.2.11'
gem 'httparty', '0.10.2'

Server gems used (and versions)

gem 'rails', '3.2.11'

Other Notes

When I use cURL to do a get request to get all model objects, it works perfectly. When I try to do a POST request with cURL...well it doesn't work because I am not using cURL properly and I don't know how to.

Update

As Vadim Chumel suggested, I modified the save method by adding .json to the request url:

# ...
self.class.post("/api/requests.json", { body: {
# ...

Now I get a 500 error on the server (API side); here are the log of the request:

Started POST "/api/requests.json" for 127.0.0.1 at 2013-01-28 16:23:23 +0000
Processing by Api::V1::RequestsController#create as JSON
  Parameters: {"request"=>{"artist_name"=>"Knife Party", "email"=>"robert@example.com", "phone_number"=>"07949967627", "country"=>"France", "city"=>"Paris"}}
   (0.2ms)  BEGIN
  Request Exists (1.0ms)  SELECT 1 AS one FROM "requests" WHERE LOWER("requests"."email") = LOWER('robert@example.com') LIMIT 1
  SQL (1.1ms)  INSERT INTO "requests" ("artist_name", "city", "country", "created_at", "email", "phone_number", "updated_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id"  [["artist_name", "Knife Party"], ["city", "Paris"], ["country", "France"], ["created_at", Mon, 28 Jan 2013 16:23:23 UTC +00:00], ["email", "robert@example.com"], ["phone_number", "07949967627"], ["updated_at", Mon, 28 Jan 2013 16:23:23 UTC +00:00]]
   (0.4ms)  COMMIT
Completed 500 Internal Server Error in 14ms

NoMethodError (undefined method `request_url' for #<Api::V1::RequestsController:0x007f84c70d2660>):
  <a href="txmt://open?url=file:///Users/aziz/Sandbox/ruby/rails/Azoui/warden/app/controllers/api/v1/requests_controller.rb&amp;line=9&amp;column=1">app/controllers/api/v1/requests_controller.rb:9:in `create'</a>


  Rendered /Users/aziz/.rvm/gems/ruby-1.9.3-p327@warden/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.0ms)
  Rendered /Users/aziz/.rvm/gems/ruby-1.9.3-p327@warden/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (0.8ms)
  Rendered /Users/aziz/.rvm/gems/ruby-1.9.3-p327@warden/gems/actionpack-3.2.11/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (14.1ms)

The weird thing is that in the whole server project, there isn't a mention of request_url


Solution

  • Try to setup location using respond_with

     @request =  Request.create(params[:request])
     respond_with(@request, :location => nil)
    

    Also your client side needs show action, that's why you have routing error

     ActionController::RoutingError (No route matches {:action=>"show", :controller=>"requests", :id=>#<Request artist_name: "Knife Party", email: "person@example.com", phone_number: "07949967627", country: "France", city: "Paris">}):
      app/controllers/requests_controller.rb:20:in `create'
    

    Also I don't understand how you save object on client side.. There is no logic about setting created promary key record.