最近,同事跟我说,他们负责的一个Api程序出现了一些很奇怪的事情。这个Api是为环保局做的一个扬尘质控大屏提供数据的,底层是基于Nancy做的。因为发现有些接口的数据出现异常,他就去调试了一下,发现当前端传递的参数如果是空,后端反序列化的时候会出现参数值和参数名是一样的情况,这就会导致查询的数据错误。没有找到原因之前,只能通过nameof来判断做处理。具体情况,见下图。
问题就是这么个问题,其实就是因为传递的参数不合规则导致的。正常情况下,参数应该是参数名1=参数值1&参数名2=参数值2,但是这里传递的空参数缺少了=,导致后端识别解析出了问题,只要按照正常的参数名1=参数值1&参数名2=参数值2即可解决问题,所以这里的bug是加了双引号,并不能完全是Nancy的锅。由于前端是基于公共的saas软件服务开发的,参数格式我们也无法修改的,于是就想在后端做一些处理来解决这个问题。
查源码我们在Module里通过下面的代码来定义一个Api,使用Bind<T>来反序列化请求的参数模型。
/// <summary> /// GET请求示例 /// </summary> /// <param></param> /// <returns></returns> public Response GetSamp(dynamic _) { var req = this.Bind<SampleInDto>(); return Response.AsJson(req); }F12找到Bind<T>定义的位置,见下图
// // 摘要: // Bind the incoming request to a model // // 参数: // module: // Current module // // 类型参数: // TModel: // Model type // // 返回结果: // Bound model instance public static TModel Bind<TModel>(this INancyModule module) { return module.Bind(); }
这个方法是接口所定义的一个方法INancyModule,再次F12进入,可以发现INancyModule有一个上下文对象NancyContext。
这个就是整个HTTP请求的上下文对象,进入NancyContext可以发现他包含HTTP请求中的的Request和Response对象
我们再进入Request对象,可以发现它包含一些常见对象,比如form,cookies,header,Query,method等等。
我们可以发现,Form和Query的类型是dynamic动态类型,其中Form定义的是一个DynamicDictionary类型的动态类型,从字面理解也很容易想到,这两个对象保存的就是我们HTTP请求中通过地址和表单提交的参数键值对字典。因为我们的HTTP请求都是通过GET方式发起的地址传参,所以暂时不去理会Form,先去找到Query赋值的地方。最好的方式就是通过源码来查看,那就先去https://github.com/NancyFx/Nancy下载Nancy的源码吧。源码下载完毕,先去除一些无用的项目集,之后便是Nancy的源码吧。还是很意外的,没想到一个打着轻量级名号的WebApi框架,源码居然这么庞大。
源码在手,我们先找到Query定义的位置,通过查找引用,我们可以发现是通过一个AsQueryDictionary()的方法进行了赋值的。
接下来通过多次F12可以发现数据来自System.Uri的Query对象,这个Query的类型是string。说白了,这个就是url中从?开始的部分。比如?name=张三&age=20,相信大家都很容易理解这些吧。
/// <summary> /// Initializes a new instance of the <see cref="Url" /> class, with /// the provided <paramref/>. /// </summary> /// <param>A <see cref="string" /> containing a URL.</param> public Url(string url) { var uri = new Uri(url); this.HostName = uri.Host; this.Path = uri.LocalPath; this.Port = uri.Port; this.Query = uri.Query; this.Scheme = uri.Scheme; }