下面,重点就是这个AsQueryDictionary()的方法了,它的作用就是对字符串部分的参数进行重新组装,放到动态字典类型的Query中,此Query非彼Query。我猜测,出问题的地方就在这个AsQueryDictionary()里面,为了验证猜想,我决定把相关的代码扒下来,通过一个简单的控制台程序来验证。这个过程比较DT,一个方法调用另外一个方法,一个类引用另外一个类,只能一个一个尝试,把需要的代码拿过来,最终的结果就是下面的样子了。
测试方法,直接写在了Main方法中,如下所示。
class Program { static void Main(string[] args) { string format = "http://localhost:5050/hello?{0}"; var context = new NancyContext(); string[] arrNormal = new string[] { string.Format(format, "name=张三&age=20"), string.Format(format, "name=张三&age="), string.Format(format, "name&age"), string.Format(format, "name=&age") }; Console.WriteLine(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>明文参数>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); foreach (string _url in arrNormal) { Console.WriteLine($"=============={_url}=============="); var url = new Url(_url); context.Request = new Request(url); var dic = context.Request.Query as DynamicDictionary; foreach (string key in dic.Keys) { Console.WriteLine($"{key}={dic[key]}"); } } Console.ReadKey(); } }我准备了几种传参的方式,把最后得到的Query打印出来,如下图。
很明显,我们复现了这个Nancy的小“bug”。现在我们已经知道是因为AsQueryDictionary()的缘故了,接下来就是断点调试一下,看看到底是怎么回事。
最核心的两个方法是下面的这两个方法,代码很简单,相信大家都能理解,我简单说一下,第一个方法是根据参数的连接符&分组,然后遍历这个数组,将每个组的内容再根据键值对的连接符=来分组,分别取出参数名和参数值,放入到字典中。
internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result) { if (query.Length == 0) return; var decoded = HtmlDecode(query); var segments = decoded.Split(new[] { '&' }, StringSplitOptions.None); foreach (var segment in segments) { var keyValuePair = ParseQueryStringSegment(segment, encoding); if (!Equals(keyValuePair, default(KeyValuePair<string, string>))) result.Add(keyValuePair.Key, keyValuePair.Value); } } private static KeyValuePair<string, string> ParseQueryStringSegment(string segment, Encoding encoding) { if (String.IsNullOrWhiteSpace(segment)) return default(KeyValuePair<string, string>); var indexOfEquals = segment.IndexOf('='); if (indexOfEquals == -1) { var decoded = UrlDecode(segment, encoding); return new KeyValuePair<string, string>(decoded, decoded); } var key = UrlDecode(segment.Substring(0, indexOfEquals), encoding); var length = (segment.Length - indexOfEquals) - 1; var value = UrlDecode(segment.Substring(indexOfEquals + 1, length), encoding); return new KeyValuePair<string, string>(key, value); }本来,这样操作是正常操作,没啥毛病。但是,我们注意看这一段代码。
var indexOfEquals = segment.IndexOf('='); if (indexOfEquals == -1) { var decoded = UrlDecode(segment, encoding); return new KeyValuePair<string, string>(decoded, decoded); }它发现参数分组中没有=连接符,会先进行一波url参数解码,然后将解码的内容既当key又作value放入了字典中,这也太骚了,我无法理解为什么会这么写,可能是我的段位太低了,始终无法理解其含义。但是这就是导致这个问题的根本原因,我们只需要对这一段代码进行一下稍微的改造即可。
var indexOfEquals = segment.IndexOf('='); if (indexOfEquals == -1) { segment = UrlDecode(segment, encoding); indexOfEquals = segment.IndexOf('='); if (indexOfEquals == -1) { return new KeyValuePair<string, string>(segment, ""); } }我们在发现参数分组中没有正常的key=value组合时,将value部分置空即可。接下来,再次运行程序,我们会发现问题已经解决了。