書接上文,按照上篇文章的基礎類搭建起運行環境后,你可能會發現,請求OSS服務報錯,至少我是這樣的。經過Fiddler抓包分析后發現http請求中根本沒有Dat文檔中要求的Date字段。
1、HTTP協議頭中的Date字段
發現有這個問題后立馬上Google Group看看是否為RestSharp的bug。討論組上給的答復很無語啊。
This is a bug. There is currently a pull request for this. https://github.com/restsharp/RestSharp/pull/275
This will only be supported in .NET 4.0, when the next version ships(no timeline).
看來是可以解決的。首先查看上面鏈接的代碼,仔細看在http頭設置中有一下代碼,其中r為System.Net.HttpWebRequest類型,v為要設置的值:
#if NET4 _restrictedHeaderActions.Add("Date", (r, v) => { DateTime parsed; if (DateTime.TryParse(v, out parsed)) { r.Date = parsed; } }); _restrictedHeaderActions.Add("Host", (r, v) => r.Host = v); #else
原來的代碼:
_restrictedHeaderActions.Add("Date", (r, v) => { /* Set by system */ }); _restrictedHeaderActions.Add("Host", (r, v) => { /* Set by system */ });
我們查看MSDN對於Headers中對Date和Host的說明http://msdn.microsoft.com/zh-cn/library/system.net.httpwebrequest.headers(v=vs.80)
.NET Framework 2.0-3.5中說明:
Date |
由系統設置為當前日期。 |
Host |
由系統設置為當前主機信息。 |
.NET Framework 4.0-4.5
Date |
由 Date 屬性設置。 |
宿主 |
由 Host 屬性設置。 |
由於RestSharp默認的編譯目標框架為.NET Framework3.5 Client Profile ,HttpWebRequest是沒有Date和Host屬性的。猜測我們在.Net 4.0環境下運行此原版后造成Date屬性未設置,因此http請求中沒有Date字段。
解決方法1:編譯框架改為.NET Framework4。然后按上面的代碼進行修改
解決方法2:通用方法適用於.NET 2.0 4.0——利用反射機制
代碼如下:
_restrictedHeaderActions.Add("Date", (r, v) => { /* Set by system */ float dotnetVersion = float.Parse(DetermineFramework()); if (dotnetVersion >= 4.0) { PropertyInfo pinfo = r.GetType().GetProperty("Date"); pinfo.SetValue(r, DateTime.Parse(v).ToUniversalTime(), null); } else { Type type = r.Headers.GetType(); MethodInfo method = type.GetMethod("AddWithoutValidate", BindingFlags.Instance | BindingFlags.NonPublic); method.Invoke(r.Headers, new[] { "Date", v }); } });
通過上面的代碼我們很的解決了Date頭的設置問題了~
2、Parameter 參數的生命周期和使用方法
我們首先看一下Restsharp中Parameter類型的定義和使用方式,在Parameter類型中Type屬性用來定義可以添加到http協議的參數的類型:

public class Parameter { /// <summary> /// Name of the parameter /// </summary> public string Name { get; set; } /// <summary> /// Value of the parameter /// </summary> public object Value { get; set; } /// <summary> /// Type of the parameter /// </summary> public ParameterType Type { get; set; } /// <summary> /// Return a human-readable representation of this parameter /// </summary> /// <returns>String</returns> public override string ToString() { return string.Format("{0}={1}", Name, Value); } }
重點關注一下ParameterType枚舉:
public enum ParameterType { Cookie, GetOrPost, UrlSegment, HttpHeader, RequestBody }
對於Cookie HttpHeader和RequestBody就沒有什么好說的了都是http的基本組成部分,我們主要關注GetOrPost和UrlSegment類型
1、GetOrPost
我們知道http協議中有兩種基本的協議Get協議和Post協議,在Get協議中通過URL中的QueryString來將參數對發送到服務器即:url?name1=value1&name2=value2。在Post協議中將參數構成name1=value1&name2=value2形式,然后放在http的Body中。
當Parameter的類型設置為GetOrPost的時候,會根據Request.Method的類型來決定參數的位置。那么我們就會產生以下疑問對於非Post和Get方法,比如PUT,Delete參數放在什么位置呢?,如果想在Post中添加QueryString如何處理?那么就要應用到UrlSegment了。
2、UrlSegment
UrlSegment是起到應用中占位的作用,在執行請求時{entity}將被替換
var rq = new RestRequest("health/{entity}/status"); rq.AddParameter("entity", "s2", ParameterType.UrlSegment);
拿OSS實例做講解,我們實現一個Bucket的Delete方法:
public void DeleteBucket(string bucket) { if (!Utils.ValidateBucketName(bucket)) { throw new ArgumentException("Unsupported bucket name:" + bucket); } var request = new RestRequest(); request.Resource = "/{bucket}/"; request.AddParameter("bucket",bucket, ParameterType.UrlSegment); //上面好看,但是不中用啊!
// request.Resource = "/" + bucket + "/";
request.Method = Method.DELETE; var response = Execute(request); }
本以為萬事大吉了,然而服務器端卻報來錯誤,“SignatureDoesNotMatch”!這下郁悶了,算法簽名應該沒有問題啊!,我們跟蹤簽名算法類型AliyunAuthenticator;
行 38 : string resource = request.Resource;
此時的值為“/{bucket}/”,無奈此時UrlSegment並沒有被替換為真正地url,因此簽名也是錯誤的,看來UrlSegment並不是那么好用的,直接寫為:
request.Resource = "/" + bucket + "/";還是實用一些。
最后,想在請求中實用QueryString參數,還是直接寫在Resource屬性里面吧,如:request.Resource = "/" + bucket + "/?acl";。看來UrlSegment好看但是不中用。
以上都是在調用OSS API上遇到的問題,有些問題沒有深入分析,還請指正,下一篇會分析我在實用文件上傳時遇到的問題!