現階段Mono版本下的WebAPI開發中存在的一些問題

背景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)親自測試,若有錯誤請不吝指出。

相關文章
相關標籤/搜索