RestSharp使用详解(2)RestSharp的BUG和不足
书接上文,按照上篇文章的基础类搭建起运行环境后,你可能会发现,请求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上遇到的问题,有些问题没有深入分析,还请指正,下一篇会分析我在实用文件上传时遇到的问题!