问题虽然解决,但是我们发现源码中有很多对参数进行解码的操作。于是,我就在想如果参数编码之后,能否正常解析呢?于是,我就准备了几组不同格式的参数进行了验证。
我们会发现当参数部分进行url编码之后,已经是无法正常解析了。于是,再次对源码进行调试分析。很快,就定位到下面这一段代码。
var decoded = HtmlDecode(query); var segments = decoded.Split(new[] { '&' }, StringSplitOptions.None);断点之后,发现当经过url编码的参数字符串query到这里,无法查找到参数连接符&,导致所有的参数都变成了同一个参数。解决办法也很简单,就是不存在&连接符的时候,再次进行解码即可。
var decoded = HtmlDecode(query); if (decoded.IndexOf('&') == -1) { decoded = UrlDecode(decoded, encoding); } var segments = decoded.Split(new[] { '&' }, StringSplitOptions.None);
至此,问题得到解决。但是新的问题产生了,怎么解决这个问题?
NNancy官方已经停止维护,不再更新了。
我们也没办法再提issue了。
解决办法很明显,这两个私有方法,我们是无法重写的,那我们怎么办呢?Nancy和ASP.NET MVC框架一样是有过滤器的,我们可以在拦截器中对上下文中的Query进行修改。
首先,我们来新建一个扩展方法类,拿来主义,直接把源码中HttpUtility.cs文件拿过来,新建两个修复方法:ParseQueryStringFix,ParseQueryStringSegmentFix,完整代码如下。
using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Text; using HttpUtility = Nancy.Helpers.HttpUtility; namespace Nancy.FixQueryDictionary { /// <summary> /// Nancy Http请求参数字典解析错误修复扩展方法 /// </summary> public static class NancyFixQueryDictionaryExtensions { /// <summary> /// 修复Http请求参数字典解析错误 /// </summary> /// <param>NancyContext对象</param> /// <returns>NancyContext对象</returns> public static NancyContext FixQueryDictionary(this NancyContext ctx) { if (ctx == null) { return ctx; } ctx.Request.Query = ctx.Request.Url.Query.AsQueryDictionary(); return ctx; } /// <summary> /// /// </summary> /// <param></param> /// <returns></returns> public static DynamicDictionary AsQueryDictionary(this string queryString) { var coll = ParseQueryString(queryString); var ret = new DynamicDictionary(); var found = 0; foreach (var key in coll.AllKeys.Where(key => key != null)) { ret[key] = coll[key]; found++; if (found >= StaticConfiguration.RequestQueryFormMultipartLimit) { break; } } return ret; } /// <summary> /// /// </summary> /// <param></param> /// <returns></returns> public static NameValueCollection ParseQueryString(string query) { return ParseQueryString(query, Encoding.UTF8); } /// <summary> /// /// </summary> /// <param></param> /// <param></param> /// <returns></returns> public static NameValueCollection ParseQueryString(string query, bool caseSensitive) { return ParseQueryString(query, Encoding.UTF8, caseSensitive); } /// <summary> /// /// </summary> /// <param></param> /// <param></param> /// <returns></returns> public static NameValueCollection ParseQueryString(string query, Encoding encoding) { return ParseQueryString(query, encoding, StaticConfiguration.CaseSensitive); } /// <summary> /// /// </summary> /// <param></param> /// <param></param> /// <param></param> /// <returns></returns> /// <exception cref="ArgumentNullException"></exception> public static NameValueCollection ParseQueryString(string query, Encoding encoding, bool caseSensitive) { if (query == null) throw new ArgumentNullException("query"); if (encoding == null) throw new ArgumentNullException("encoding"); if (query.Length == 0 || (query.Length == 1 && query[0] == '?')) return new NameValueCollection(StringComparer.Ordinal); if (query[0] == '?') query = query.Substring(1); NameValueCollection result = new NameValueCollection(StringComparer.Ordinal); ParseQueryStringFix(query, encoding, result); return result; } #region 原方法 internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result) { if (query.Length == 0) return; var decoded = HttpUtility.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 = HttpUtility.UrlDecode(segment, encoding); return new KeyValuePair<string, string>(decoded, decoded); } var key = HttpUtility.UrlDecode(segment.Substring(0, indexOfEquals), encoding); var length = (segment.Length - indexOfEquals) - 1; var value = HttpUtility.UrlDecode(segment.Substring(indexOfEquals + 1, length), encoding); return new KeyValuePair<string, string>(key, value); } #endregion #region 修复方法 internal static void ParseQueryStringFix(string query, Encoding encoding, NameValueCollection result) { if (query.Length == 0) return; var decoded = HttpUtility.HtmlDecode(query); if (decoded.IndexOf('&') == -1) { decoded = HttpUtility.UrlDecode(decoded, encoding); } var segments = decoded.Split(new[] { '&' }, StringSplitOptions.None); foreach (var segment in segments) { var keyValuePair = ParseQueryStringSegmentFix(segment, encoding); if (!Equals(keyValuePair, default(KeyValuePair<string, string>))) result.Add(keyValuePair.Key, keyValuePair.Value); } } private static KeyValuePair<string, string> ParseQueryStringSegmentFix(string segment, Encoding encoding) { if (String.IsNullOrWhiteSpace(segment)) return default(KeyValuePair<string, string>); var indexOfEquals = segment.IndexOf('='); if (indexOfEquals == -1) { segment = HttpUtility.UrlDecode(segment, encoding); indexOfEquals = segment.IndexOf('='); if (indexOfEquals == -1) { return new KeyValuePair<string, string>(segment, ""); } } var key = HttpUtility.UrlDecode(segment.Substring(0, indexOfEquals), encoding); var length = (segment.Length - indexOfEquals) - 1; var value = HttpUtility.UrlDecode(segment.Substring(indexOfEquals + 1, length), encoding); var res = new KeyValuePair<string, string>(key, value); return res; } #endregion } }