一直以来想要做个程序,将Google Docs用作网盘,程序做得差不多了才发现不是所有的人都可以上传任意类型的文件,只有商业用户才可以。商业用户是要交钱的,这与我们倡导的免费精神相去甚远。怎么办?我的心血不能白费,Google还算厚道没有把门关死,可以通过form的形式上传,我们可以模拟form的动作,就能上传了。
Google在上传时要进行身份验证。取得身份验证后,提出上传要求,这时返回一个上传地址,然后上传文件。下面一步步来:
访问Google Docs获取登录页面: 先访问 http://docs.google.com,它通过自动跳转将你带到一个登录页面。按登录页面中的头部的set-cookie设置HttpWebRequest的cookie,cookie中最重要的是HSID。
提交登录信息:
设置url为 https://www.google.com/accounts/ServiceLoginAuth?service=writely,然后设置提交数据:
ltmpl=homepage&continue=http://docs.google.com/&followup=http://docs.google.com/&service=writely&nui=1&rm=false&dsh={0}<mpl=homepage<mpl=homepage&GALX={1}&Email={2}&Passwd={3}&rmShown=1&signIn=登录&asts=
其中Email填Google帐户名,Passwd填密码,GALX可以从cookie中找到,dsh可从页面的数据中得到。
检查Cookie: 访问网址:https://www.google.com/accounts/CheckCookie,取得下一步的访问地址。
获取认证Cookie:
访问网址:http://docs.google.com/?auth=.......
,根据回应设置cookie,这时的cookie就是可以访问相当于doc api中的token了。
关键Cookie:认证后的cookie中包含三条记录HSID、SID、writelySID
private CookieContainer GetAuthenticationCookie(string User,string Passwd) { string GALX; string dsh; string resText = ""; CookieContainer Auth = new CookieContainer(); //第一步:访问docs.google.com获取登录页面 HttpWebRequest req = CreatRequest("http://docs.google.com/"); req.Method = "POST"; req.ContentLength = 0; HttpWebResponse res = (HttpWebResponse)req.GetResponse(); //第二步:提取GALX和dsh参数,构造登录数据 resText = getResponseText(res); GALX = resText.Substring(resText.IndexOf("GALX") + 26, 11); if ((resText.Substring(resText.IndexOf("dsh") + 32, 1)) == "-") dsh = resText.Substring(resText.IndexOf("dsh") + 32, 20); else dsh = resText.Substring(resText.IndexOf("dsh") + 32, 19); string postData = string.Format( "ltmpl=homepage&continue=http://docs.google.com/&followup=http://docs.google.com/&service=writely&nui=1&rm=false&dsh={0}<mpl=homepage<mpl=homepage&GALX={1}&Email={2}&Passwd={3}&rmShown=1&signIn=登录&asts=", dsh, GALX, User, Passwd); req = CreatRequest("https://www.google.com/accounts/ServiceLoginAuth?service=writely"); req.AllowAutoRedirect = false; req.Method = "POST"; //设置cookie及提交数据 Auth.Add(setCookie(res, "www.google.com")); req.CookieContainer = Auth; setPostData(req, postData); res = (HttpWebResponse)req.GetResponse(); //第三步:检查Cookie req = CreatRequest(res.Headers["Location"]); Auth.Add(setCookie(res, "www.google.com")); req.CookieContainer = Auth; res = (HttpWebResponse)req.GetResponse(); //第四步:获取最终认证Cookie resText = getResponseText(res); string url = resText.Substring(resText.IndexOf("url=")).Split('"')[0]; url = HttpUtility.HtmlDecode(url); url = url.Substring(5, url.Length - 6); req = CreatRequest(url); req.Method = "GET"; req.AllowAutoRedirect = false; Auth.Add(setCookie(res, "www.google.com")); req.CookieContainer = Auth; res = (HttpWebResponse)req.GetResponse(); Auth.Add(setCookie(res, "www.google.com")); return Auth; }
整个过程很简单,但调试花了很长时间,主要是在HTTPS下调试走了很多弯路。一开始使用HttpAnalyzer,结果有bug显示的传送数据总是重复。后来还是使用Fiddler才解决。但使用Fiddler时在HTTPS下会出现证书与网站不符的错误。
解决办法:
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate); public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return true; }
第一个函数在设置HttpWebRequest时调用就不会出现错误了