因为项目需要在后台实现自动将内容同时分享到微博,QQ空间,豆瓣等各个社交网络,一开始准备找个现成的gem,发现了hooopo写的:
https://github.com/hooopo/oauth_china 但是这个项目微博用的是OAuth 1,新浪很快就不支持了,原准备fork一下修改,但觉得依赖太多,而且我们的项目本身用了Omniauth做登录,用户的token等信息都已经拿到,其实只要用一个http客户端,将auth的信息放在header里面调用相对的api就行了。于是想起了以前用过的gem:
Faraday
先介绍一下Faraday, 它是一个ruby的http客户端,用类似rack middleware的方式来定制各种http调用。它本身并不实现http客户端,而是用adapter的方式对Net::HTTP, patron甚至 em-http 等提供了封装。非常简单易用,并且容易定制,当然再漂亮的介绍文字都比不过代码,还是上代码,看看它的用途吧。
首先是最基本发布文本到新浪微博,因为weibo接受将token作为一个参数传递,所以就2行代码搞定:
def weibo_update(text)
conn = Faraday.new(:url => 'https://api.weibo.com')
conn.post '/2/statuses/update.json', :access_token => self.token, :status => text
end
然后是发布文本,同时上传一张图片到新浪微博,这个时候需要用到它的Middleware,在定义http connection中设置http request类型为multipart,同时使用Net::HTTP作为adapter(因为默认client的不支持multipart),然后文件使用Faraday::UploadIO,5行代码搞定:
def weibo_upload(text, image_path)
conn = Faraday.new(:url => 'https://api.weibo.com') do |f|
f.request :multipart
f.adapter :net_http
end
conn.post '/2/statuses/upload.json', :access_token => self.token, :status => text, :pic => Faraday::UploadIO.new(image_path, 'image/jpeg')
end
同样的,分享到QQ空间,腾讯微博都只要用类似的方法处理,代码行数也都是2~5行(见文章最后)。
我们再来看看比较复杂的豆瓣 api,豆瓣用的Oauth 1只支持在header里面传递token,并且提交的内容不是key/value那样的form形式,而是xml,代码写成这样:
def douban_saying(content)
conn = Faraday.new(:url => 'http://api.douban.com') do |f|
f.request :oauth, :consumer_key => self.app_id, :consumer_secret => self.app_key, :token => self.token, :token_secret => self.secret
f.request :url_encoded
f.adapter :net_http
end
conn.post do |req|
req.url '/miniblog/saying'
req.headers['Content-Type'] = 'application/atom+xml'
req.body = <<-XML
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
<content>#{content}</content>
</entry>
XML
end
end
在request里面我们使用了oauth的middleware(由gem faraday_middleware提供),在header中插入oauth,同时在请求的时候,指定Content-Type,并将body内容设置成xml。但是在使用的时候发现一个问题,豆瓣必须要求header中的Authorization有OAuth realm=xxx的内容,于是我们得自己写一个middleware了,Faraday的middleware和rack很类似,只要实现一个call方法,在这里,我们用正则替换,在已经生成好的Authorization header中替换插入一段realm即可:
class DoubanOauthRealmMiddleware
def initialize(app, options = {})
@app = app
@options = options
end
def call(env)
env[:request_headers]["Authorization"] = env[:request_headers]["Authorization"].gsub(/^OAuth/, "OAuth realm=\"http://api.douban.com\",")
@app.call(env)
end
end
然后将我们写的这个middleware,加入到上面的代码中,就搞定了:
f.request :url_encoded
f.use DoubanOauthRealmMiddleware
f.adapter :net_http
简单的介绍就结束了,其实Faraday还有更多非常棒的功能,比如对于response的自动json解析,自动retry,timeout处理等,都可以通过非常简洁的方式配置和middleware来实现,可以阅读它的源码来了解更多。
附录:发布到QQ空间,腾讯微博的代码,供有用的同学参考:
def qq_add_share(summary, images, title, url)
conn = Faraday.new(:url => 'https://graph.qq.com')
conn.post '/share/add_share', :title => title, :url => url, :summary => summary, :images => images, :source => 1, :type => 4, :nswb => 1,
:access_token => self.token, :openid => self.provider_uid, :oauth_consumer_key => self.app_id
end
def qq_add_t(content)
conn = Faraday.new(:url => 'https://graph.qq.com')
conn.post '/t/add_t', :content => content, :syncflag => 1, :access_token => self.token, :openid => self.provider_uid, :oauth_consumer_key => self.app_id
end
def qq_add_pic_t(content, image_path)
conn = Faraday.new(:url => 'https://graph.qq.com') do |f|
f.request :multipart
f.adapter :net_http
end
conn.post '/t/add_pic_t', :content => content, :pic => Faraday::UploadIO.new(image_path, 'image/jpeg'), :syncflag => 1, :access_token => self.token, :openid => self.provider_uid, :oauth_consumer_key => self.app_id
end