天天看點

WebAPi傳回類型到底應該是什麼才合适,這是個問題?

前言

有些問題隻有真正遇到或者用到并且多加思考才會想到,平常若作為自學的心态去學習則不會考慮太多,我慢慢明白對于那些有太多要學的東西或者說的更加明确而且具體一點的話,如果對于你現在不是迫切要學或者需要掌握的技能,那就暫且放在一邊吧,比如現在比較火的angular和react,我之前也花時間去學了,但是公司壓根不用或者有專門的前端你學多了貌似沒什麼很大的實際用途,其實僅僅做一點基本的了解即可,至少别人問起也知道一二,不要看到别人學什麼或者火了什麼就盲目跟風,還是根據自身實際情況來學習才是王道。這不剛說到根據自身來學習,腦袋妄想着正在做的項目,突然冒出一個想法,為什麼那不可以,為什麼它又存在呢?這篇文章就出來了。

話題介紹

我們知道在WebAPi中對于響應結果我們都是這樣用的:

public HttpResponseMessage GetResult<T>(T t)             {                 return Request.CreateResponse<T>(HttpStatusCode.OK, t);             }      

在項目中前端為了和其他統一,封裝了一套響應的結果和狀态碼,要求直接傳回對象,于是将上述修改成比如如下:

public Result<List<Person>> GetResult()         {            var result = new Result<List<Person>>();            return result;         }         public class Result<T> : BaseResult         {             public T Data;         }         public class BaseResult         {             public string Message;             public int Status;             public ErrorCode ErrorCode;         }         public enum ErrorCode         {         ......         }      

統觀以上兩種方法,一種是WebAPi内置響應的結果,另外一種則是直接傳回自定義響應結果。

于是乎,我開始思索這兩種方法雖然都能得到我們想要的結果,但是有什麼差別沒有呢?說的更加明确一點的是,二者在資料響應上有沒有性能上的差異呢?

WebAPi響應結果和自定義響應結果二者性能差異

以上則是需要傳回對象來進行處理,而有些我們則不需要傳回任何對象來進行處理例如直接傳回void,而在WebAPi中對應需要傳回 IHttpActionResult 例如自定義傳回則是如下:

public void GetFirst()        {.....}      

在WebAPi中則是進行如下傳回:

public IHttpActionResult GetSecond()        {          return OK();                    }      

下面我們在控制台中分别來測試這二者在WebHost以及在SelfHost上的差異,我們如何擷取其差異呢?我們通過對void方法和http方法在控制台中發出1000個請求來擷取其總共花費時間來進行比較。

SelfHost

[HttpGet]             public void GetFirst()             {                 StringBuilder stringbuilder = new StringBuilder();                 for (int i = 0; i < 20; i++)                 {                     stringbuilder.Append("something");                 }             }             [HttpGet]             public IHttpActionResult GetSecond()             {                 StringBuilder stringbuilder = new StringBuilder();                 for (int i = 0; i < 20; i++)                 {                     stringbuilder.Append("something");                 }                 return Ok();             }      

在控制台中方法如下:

private const string voidUrl = "http://localhost:8080/api/home/GetFirst";             private const string httpUrl = "http://localhost:8080/api/home/GetSecond";             private static List<TimeSpan> voidTimes = new List<TimeSpan>();             private static List<TimeSpan> httpTimes = new List<TimeSpan>();             static void Main(string[] args)             {                 Console.WriteLine("Start Test....");                 for (int i = 0; i < 1000; i++)                 {                     voidTimes.Add(getResponse(voidUrl));                     Thread.Sleep(10);                     Console.WriteLine("void Test " + i);                 }                 Console.WriteLine("Finished Void Test");                 for (int i = 0; i < 1000; i++)                 {                     httpTimes.Add(getResponse(httpUrl));                     Thread.Sleep(10);                     Console.WriteLine("http Test " + i);                 }                 Console.WriteLine("Finished Http Test");                 var voidTotalTime = voidTimes.Sum(t => t.Milliseconds);                 Console.WriteLine("void方法發出1000個請求總共需要時間:" + voidTotalTime);                 Console.WriteLine("void方法平均每一個請求需要時間:" + voidTotalTime / 1000.00 + "秒");                 var httpTotalTime = httpTimes.Sum(t => t.Milliseconds);                 Console.WriteLine("http方法發出1000個請求總共需要時間: " + httpTotalTime);                 Console.WriteLine("http方法平均每一個請求需要時間: " + httpTotalTime / 1000.00 + "秒");                 Console.Read();             }             static TimeSpan getResponse(string url)             {                 var stopWatch = new Stopwatch();                 stopWatch.Start();                 var httpClient = new HttpClient();                 httpClient.BaseAddress = new Uri(url);                 var task = httpClient.GetAsync(httpClient.BaseAddress).Result;                 var result = task.Content.ReadAsAsync(typeof(object));                 var timeSpan = stopWatch.Elapsed;                 stopWatch.Stop();                 return timeSpan;             }      

下面我們來直覺示範整個過程:

WebAPi傳回類型到底應該是什麼才合适,這是個問題?

 從上看出似乎由http方法節約一點時間,我們将上述中的方法循環次數,進行如下修改:

for (int i = 0; i < 200000; i++)                 {                     stringbuilder.Append("something");                 }      

這時候我們再來看看結果:

WebAPi傳回類型到底應該是什麼才合适,這是個問題?

當有二十萬條資料時此時時間又多節約一點點。接下來我們再來測試WebHost。

WebHost

在WebHost中我們利用特性來管理請求方法:

[HttpGet]             [Route("test/void")]             public void GetFirst()             {                 StringBuilder stringbuilder = new StringBuilder();                 for (int i = 0; i < 20; i++)                 {                     stringbuilder.Append("something");                 }             }             [HttpGet]             [Route("test/IHttpActionResult")]             public IHttpActionResult GetSecond()             {                 StringBuilder stringbuilder = new StringBuilder();                 for (int i = 0; i < 20; i++)                 {                     stringbuilder.Append("something");                 }                 return Ok();             }      

此時将控制台請求位址進行對應修改即可:

private const string voidUrl = "http://localhost:2531/test/void";      private const string httpUrl = "http://localhost:2531/test/IHttpActionResult";      

此時示範結果如下:

WebAPi傳回類型到底應該是什麼才合适,這是個問題?

此時快了接近一秒。此時我們将資料增加到同樣20萬時再看看:

WebAPi傳回類型到底應該是什麼才合适,這是個問題?

此時還是快了1秒。到了這裡是不是就算結束了呢,我們再來看看

當我們請求void方法時傳回的狀态碼為如下:

WebAPi傳回類型到底應該是什麼才合适,這是個問題?

此時利用http來進行響應則是如下:

WebAPi傳回類型到底應該是什麼才合适,這是個問題?

其傳回狀态也不同,我們則需要有對應的處理方式。

總結

在示範void方法和http方法時有時也會出現http方法時間比void方法慢的原因,不知是何緣故,理論上來說用HttpResponseMessage來作為響應結果會快一點,因為HttpResponseMessage内置對于一些異常都做了處理并傳回對應的狀态碼而void方法則未做任何處理。但是從另外一個角度看,若我們自定義一套傳回的狀态碼來進行處理也并非不可,個人覺得利用WebAPi内置的HttpResponseMessage響應機制來進行結果響應最佳,期待各位的批評和答案,同時不知上述測試是否合理。當時想到這個問題時也查了相關資料,還真有做過類似測試的,于是借用了一下。

參考資料:http://stackoverflow.com/questions/22689888/webapi-2-is-a-void-response-faster-then-ihttpactionresult

所有的選擇不過是為了下一次選擇做準備