OAuth gem で multipart や xml、json データを POST する方法

OAuth の POST リクエストでは、基本的に Content-type = x-www-form-urlencoded のデータを送ることが想定されています。

もちろんそれ以外にも Content-type が multipart/form-data や application/xml、application/json などの場合も、request body が signature base string に入らないという違いはあるものの、OAuth を使って POST することは可能です。

仕様的には。

smart.fm でも一部 API で multipart (upload file) や xml/json データ (save study data : comming soon) をポストしないといけない API があるのですが、みごと ruby の oauth gem ではそれらに失敗しました。

原因は、OAuth::Consumer#create_http_request の以下の1行。青色の部分を赤色の部分のように変えれば動きます。

module OAuth
  class Consumer
    def create_http_request(http_method, path, *arguments)
      http_method = http_method.to_sym
      
      if [:post, :put].include?(http_method)
        # data = (arguments.shift || {}).reject { |k,v| v.nil? }
        data = arguments.shift
        data.reject! { |k,v| v.nil? } if data.is_a?(Hash)
      end
      
      以下略
  end
end

というところを、github の master ではこの bug が fix されているのですが、rubyforge にはまだ反映されていなくて、ちょっとハマりました。これを fix すれば、こんな感じで POST できます。

access_token.post(path, params, {'Content-Type' => 'multipart/form-data'})
access_token.post(path, xml, {'Content-Type' => 'application/xml'})
access_token.post(path, json, {'Content-Type' => 'application/json'})

ps.
もちろん、可能な限り x-www-form-urlencoded で送る方が、バグも少ないでしょうし、セキュアです。

また、もし Service Provider が OAuth Request Body Hash という extension をサポートしている場合は、Content-type が x-www-form-urlencoded でなくても post body を signature base string に含めることで、x-www-form-urlencoded 同様セキュアにデータを送ることができます。