背景linux
因为公司积极推进各业务产品服务化,得益于容器化技术的不断发展及普及,项目组的服务也更多地基于Mono,Jexus,Docker,Kubernetes等类库、容器、管理工具运行于Linux系统上。业务服务中基于WebApi方式的服务必不可少,本文主要记录在ms的Webapi2框架下,以mono+jexus做为编译、运行框架在linux环境下提供服务这个过程当中存在的一些坑。git
mono版本的选择github
截止写本文以前,Mono的最新版本是4.6.0.125,除此版本以外,主要使用、测试的版本还包括,Mono-4.4(小版本记不清了),Mono-4.2.2。json
这3个版本以4.2,4.4,4.6三个版本指代,而且这3个版本都是官方的stable版本。c#
其中未完成的功能本文略过不提,可参考 这个连接windows
(注:下文所述全部产生问题的后端代码,在.net framework+windows平台部署测试均无任何问题,仅针对Mono+Jexus+linux环境而言)后端
Mono-4.6版本支持c#6.0语法,可是,MVC 4,5中的某些Asyn方法未实现(后边的测试会看到),这个对Webapi2框架是同样的,然而这些都不是最重要的,Mono4.6(亲测),4.4(同事测试反映)这两个大版本存在一个没法搞定的问题:api
Webapi部署后测试,由其发起的对外的Http请求工做正常,可是在一段时间后(不定,通常一小时内吧),不会再发出Http请求,可是response并不为空仅仅是Content为空。服务器
这个问题就相似于一票否决,虽然Mono4.6版本修复了以前(4.2)版本存在的一些问题,而且还支持了C#6.0语法等等,可是这个莫名其妙的缺陷如此致命以致于在实际开发工做中没法使用这两个版本(4.4,4.6)。若有解决方案请不吝指出。app
所以,虽然存在不少缺陷,可是Mono-4.2版本是目前(4.6.0版本及以前)惟一的选择。
Mono-4.2版本使用过程遇到的一些问题
这里并无说这些问题是Mono-4.2版本的Bug,由于有些问题是官方明确了还未实现的,因此并不能说是Bug。
在个人实际工做场景中(Webapi服务),主要的问题仍是集中于请求中Content解析上,再次强调一下,下文全部代码在.net framework下都没有任何问题,仅在Mono-4.2版本编译运行环境下存在缺陷。
请求均以Content-Type:application/json发送。
示例一:
[ActionName("GetStatusTimeDist")] [HttpPost] public IHttpActionResult GetStatusTimeDist([FromBody]ParamClass param) { ... }
正常状况下下最多见的写法,ParamClass是自定义的参数类型,FromBody属性加或者不加都不会影响结果:HttpStatusCode:500,路由匹配时抛出的异常。
2016-11-9补充:
经过errorfilter捕获服务器内部异常包装后返回能够看到详细信息:
400错误信息: ["Method 'HttpRequestBase.GetBufferlessInputStream' not found."]
在mono的开发记录中能够查询这个方法的实现:在4.2.2版本中,这个方法应该还未实现,所以出现以上错误。
示例二:
[ActionName("GetStatusTimeDist")] [HttpPost] public IHttpActionResult GetStatusTimeDist([FromBody]dynamic param) { ... }
也是一种好用的写法,不用预先定义参数类型,以动态类型获取content内的参数,但结果仍然同样:HttpStatusCode:500
示例三:
通过以上两种示例尝试,可见在api声明中加入请求体中的参数获取是没法正确匹配路由的,可是对querystring的传参方式是没有问题的,具体的测试就略过,若是改成querystring的方式传参,是能够看到200返回的。
要解决请求content传递参数,只能从Request中主动获取Content内容:
[ActionName("GetStatusTimeDist")] [HttpPost] public IHttpActionResult GetStatusTimeDist() { HttpContent bodyContent = Request.Content; Task<string> task = bodyContent.ReadAsStringAsync(); task.Wait(); content = task.Result; }
这种获取Content的方式,能够正确匹配路由,结合querystring传参方式的测试结果可证实Mono4.2并不支持在API声明中包含content传参的任何形式。可是会收到另外一个HttpStatusCode:405,异常点:task.Wait(),猜想是Mono-4.2版本中部分Asyn方法未实现形成。
示例四:
一种不太常见的stream获取方式
[ActionName("GetStatusTimeDist")] [HttpPost] public IHttpActionResult GetStatusTimeDist() { string content; Stream stream = new MemoryStream(); var action = Request.Content.CopyToAsync(stream); action.Wait(); stream.Position = 0; using (StreamReader sr = new StreamReader(stream)) { content = sr.ReadToEnd(); } stream.Dispose(); }
以这种方式获取content内容,mono会明确告诉你错误信息:CopyToAsync(Stream stream)这个方法不存在。应该是还未实现。
以上的四种方式均为.net framework中能够正常工做的获取content内容的方式,可是在mono-4.2版本中都不可行。
如下是目前测试出的惟一可行的方式:
示例五:
[ActionName("GetStatusTimeDist")] [HttpPost] public IHttpActionResult GetStatusTimeDist() { string content; using (StreamReader reader = new StreamReader(HttpContext.Current.Request.InputStream)) { content = reader.ReadToEnd(); } }
获取到的content为body中传递的json字符串,须要主动进行反序列化,获得所须要的参数对象。
总结
Mono截止目前(4.6.0.125)版本为止,功能已经愈来愈完善,好比在4.6版本中已经修复了4.2版本中存在的请求Content参数获取的各类缺陷,能够以.net framework环境下各类可用的(如示例一,二等)方式来获取请求的content参数,但却引入了一些新的缺陷(一段时间后没法主动发出http请求),而且直接致使该版本不可用于生产环境。这些问题都须要全面的测试及评估,Mono的版本更迭也须要更加谨慎。
以上内容均基于本人对现阶段各版本(截止 Mono-4.6.0.125)亲自测试,若有错误请不吝指出。