<?xml version="1.0"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
  <channel>
    <title>CKAN: Ticket #2942: API POST barfs on interesting Content-Type headers</title>
    <link>http://localhost/ticket/2942</link>
    <description>&lt;p&gt;
When POSTing to the API, if specified, the 'Content-Type' header must be blank or 'application/x-www-form-urlencoded'. Otherwise we get an error like: "Bad request - JSON Error: Could not extract request body data: Bad content type: \'; charset=utf-8\'""
&lt;/p&gt;
&lt;p&gt;
The problem is that this is a very reasonable header to send. Indeed requests 0.14 sends this particular header.
&lt;/p&gt;
&lt;p&gt;
This affects all versions of CKAN.
&lt;/p&gt;
&lt;p&gt;
This is due to webob/requests.py:1248 being pretty basic.
&lt;/p&gt;
</description>
    <language>en-us</language>
    <image>
      <title>CKAN</title>
      <url>http://assets.okfn.org/p/ckan/img/ckan_logo_shortname.png</url>
      <link>http://localhost/ticket/2942</link>
    </image>
    <generator>Trac 0.12.3</generator>
    <item>
      
        <dc:creator>dread</dc:creator>

      <pubDate>Tue, 25 Sep 2012 22:06:31 GMT</pubDate>
      <title>status changed; resolution set</title>
      <link>http://localhost/ticket/2942#comment:1</link>
      <guid isPermaLink="false">http://localhost/ticket/2942#comment:1</guid>
      <description>
          &lt;ul&gt;
            &lt;li&gt;&lt;strong&gt;status&lt;/strong&gt;
                changed from &lt;em&gt;new&lt;/em&gt; to &lt;em&gt;closed&lt;/em&gt;
            &lt;/li&gt;
            &lt;li&gt;&lt;strong&gt;resolution&lt;/strong&gt;
                set to &lt;em&gt;wontfix&lt;/em&gt;
            &lt;/li&gt;
          &lt;/ul&gt;
        &lt;p&gt;
Unfortunately with a few hours hacking I couldn't find a way to get this version of &lt;a class="missing wiki"&gt;WebOb?&lt;/a&gt; to cope with this header.
&lt;/p&gt;
&lt;p&gt;
The line I think it goes wrong is &lt;tt&gt;ctype = env.get('CONTENT_TYPE', 'application/x-www-form-urlencoded')&lt;/tt&gt; in request.py, and the commented line above looks right, but wipes the request data for some reason. I tried intercepting the bad header and deleting it, but I couldn't do that before &lt;a class="missing wiki"&gt;WebOb?&lt;/a&gt; processed it, and couldn't persuade &lt;a class="missing wiki"&gt;WebOb?&lt;/a&gt; to reprocess it once the header was edited.
&lt;/p&gt;
&lt;p&gt;
I did write a failing test though if that is useful to someone in the future:
&lt;/p&gt;
&lt;pre class="wiki"&gt;diff --git a/ckan/tests/functional/api/test_api.py b/ckan/tests/functional/api/t
index 539d184..b4cc4ce 100644
--- a/ckan/tests/functional/api/test_api.py
+++ b/ckan/tests/functional/api/test_api.py
@@ -49,6 +49,18 @@ class TestApi3(Api3TestCase, ApiTestCase):
         assert_in('Bad request - JSON Error: No request body data',
                   res.body)
+    def test_content_type_headers_can_be_sent(self):
+        '''#2942 Webob can only cope with very specific Content-Type header
+        values, so this tests that a work-around is in place.
+
+        This particular header value is the default for 'requests' 0.14.
+        '''
+        offset = self.offset('/action/package_search')
+        params = '%s=1' % json.dumps({'q': 'russian'})
+        headers = {'Content-Type': '; charset=utf-8'}
+        res = self.app.post(offset, params=params, headers=headers,
+                            status=[200])
+
&lt;/pre&gt;&lt;p&gt;
There is a clear error to the user:
&lt;/p&gt;
&lt;pre class="wiki"&gt;&amp;gt;&amp;gt;&amp;gt; requests.post('http://datahub.io/api/action/package_list', data='{}').content
'"Bad request - JSON Error: Could not extract request body data: Bad content type: \'; charset=utf-8\'"'
&lt;/pre&gt;&lt;p&gt;
The advice to users of requests would be to ensure they specify the content-type as follows:
&lt;/p&gt;
&lt;pre class="wiki"&gt;&amp;gt;&amp;gt;&amp;gt; requests.post('http://datahub.io/api/action/package_list', data='{}', headers={'content-type': 'application/x-www-form-urlencoded'}).content
&lt;/pre&gt;
      </description>
      <category>Ticket</category>
    </item><item>
      
        <dc:creator>dread</dc:creator>

      <pubDate>Tue, 25 Sep 2012 22:12:24 GMT</pubDate>
      <title></title>
      <link>http://localhost/ticket/2942#comment:2</link>
      <guid isPermaLink="false">http://localhost/ticket/2942#comment:2</guid>
      <description>
        &lt;blockquote class="citation"&gt;
&lt;pre class="wiki"&gt;&amp;gt;&amp;gt;&amp;gt; requests.post('http://datahub.io/api/action/package_list', data='{}', headers={'content-type': 'application/x-www-form-urlencoded'}).content
&lt;/pre&gt;&lt;/blockquote&gt;
&lt;p&gt;
Or even better:
&lt;/p&gt;
&lt;pre class="wiki"&gt;&amp;gt;&amp;gt;&amp;gt; requests.post('http://datahub.io/api/action/package_list', data='{}', headers={'content-type': 'application/json'}).content
&lt;/pre&gt;
      </description>
      <category>Ticket</category>
    </item>
 </channel>
</rss>