前言
今天我們來實作一個特殊的需求,這個需求說來也不過分,不過有點違背WebAPi的真實用途,WebAPi不過是作為傳輸資料而用,若非在項目開發中斷不可想到還要實作一個頁面來實時顯示清單并進行後續其他操作。接下來我們來看看。
話題介紹
當我們建立一個應用程式時可以選擇是否建立WebAPi項目,我們選擇建立WebAPi,同時在其根目錄下建立一個Index的Html頁面,于是乎則有了如下的樣子:

我們運作起來看看是否能正确顯示結果:
從這裡我們可以看出貌似不存在我們本節所需要講解的問題,這裡的介紹也就僅供我們玩玩而已,實際開發中會把WebAPi完全抽離出來作為服務來進行資料傳輸,而這裡能夠正确通路到Index頁面依然是以MVC為主導,WebAPi寄宿為WebHost,是以通路其目錄下的内容毫無疑問會通路到,如果我們将WebAPi完全隔離出來也就是不依賴于IIS,利用Slef-Host來實作。(有關WebAPi中的WebHost以及Self-Host可以參考前面系列文章)。
完全抽離WebAPi
我們來建立一個Windows應用程式起名為WebAPiReturnHtml。
我們建立立一個HttpServerHost類利用 HttpSelfHostServer 來監聽Http請求,代碼如下:
public class HttpServerHost { /// <summary> /// HttpSelfHostServer執行個體 /// </summary> private HttpSelfHostServer _server; /// <summary> /// 啟動HTTP伺服器 /// </summary> public void Start() { var config = new HttpSelfHostConfiguration("http://localhost:8080"); config.MaxReceivedMessageSize = int.MaxValue; config.Routes.MapHttpRoute("Default", "api/{controller}/{action}"); //設定最大接收消息大小 config.MaxReceivedMessageSize = int.MaxValue; config.Formatters.Clear(); config.Formatters.Add(new JsonpFormatter()); _server = new HttpSelfHostServer(config); //允許跨域 _server.Configuration.MessageHandlers.Add(new CorsHandler()); _server.OpenAsync().Wait(); } }
我們來示範下效果:
結果出錯了,此時我們應該注意應該以【管理者身份運作VS】才可。
我們緊接着添加測試類如下:
public class HomeController : ApiController { [HttpGet] public string Test() { return "OK"; } }
我們來看看示範結果:
整個完全抽離出WebAPi的過程就是這麼簡單,接着我們回到開頭的話題介紹,我們在此項目下建立Index頁面如下來通路試試:
結果如下:
此時則讓我們大失所望,完全抽離出WebAPi此時則無妨通路到靜态資源,此時我們來利用讀取檔案字元串的形式來傳回該靜态資源,如下:
public HttpResponseMessage GetHtml() { var currentRunPath = AppDomain.CurrentDomain.BaseDirectory; var substringBin = currentRunPath.IndexOf("bin"); var path = currentRunPath.Substring(0, substringBin) + "Index.html"; var httpResponseMessage = new HttpResponseMessage(); httpResponseMessage.Content = new StringContent(File.ReadAllText(path), Encoding.UTF8); httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html"); return httpResponseMessage; }
我們再來看看結果:
如上請求我們可以設定路由特性,如下:
[HttpGet] [Route("Index")]
此時通路的路徑則變為 localhost:8080/index 更加簡潔。為了實作這樣的需求隻能無所不用其極,如果是加載圖檔呢,又該如何呢?當然也有解決辦法,上述既然有讀取字元串StringContent,那肯定有讀取圖檔的流,将上述
httpResponseMessage.Content = new StringContent(File.ReadAllText(path), Encoding.UTF8); httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
修改為如下即可:
httpResponseMessage.Content = new StreamContent(new FileStream(path, FileMode.Open)); httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("image/*");
那麼現在問題來了,如何在上述Index.html頁面中去請求JS呢?既然有了這個思路那就好辦了,我們繼續往下看。
在控制器中傳回JS定義如下:
[HttpGet] public HttpResponseMessage GetJS(string file) { var currentRunPath = AppDomain.CurrentDomain.BaseDirectory; var substringBin = currentRunPath.IndexOf("bin"); var path = currentRunPath.Substring(0, substringBin) + file; var httpResponseMessage = new HttpResponseMessage(); httpResponseMessage.Content = new StringContent(File.ReadAllText(path), Encoding.UTF8); httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/javascript"); return httpResponseMessage; }
在根目錄下建立Index.js,去定義函數如下:
function btnClick() { alert("調用Index.js成功"); }
請求Index.js,以及結構如下:
接下來我們再來進行示範:
到這裡我們達到我們需求就已完全結束。
總結
這其中還是有一點小小的疑惑,如果是在WebAPi2中需要啟動 config.MapHttpAttributeRoutes(); 在上述請求Index方法時如果我們添加 [Route(index)] ,此時請求index.js則需進行如下修改
<script type="text/javascript" src="GetJS?file=/index.js"></script> 修改為 <script type="text/javascript" src="api/home/GetJS?file=/index.js"></script>
但是在WebAPi2.2中應該沒有了 config.MapHttpAttributeRoutes(); 想必是已經預設啟動了該路由特性但是此時在上述請求Index方法時若定路由定性 [Route("index")] 此時根本請求不到該Index方法,不知是何緣故!
特殊需求有特殊的實作方法,若未有此需求的提出根本想不出這樣去實作,同時不多加思考也會停滞不前感覺這樣做根本是不可能,但是并非不可能,不是嗎!可能說對于這樣在WebAPi中存放頁面不是太可取,如果能放在其他的UI,我又何必這樣做呢,需求如此,隻能這樣做了,當然也可以直接将樣式和腳本放在伺服器上通過CDN來加載,實作的僅僅是顯示一個清單進而進行其他幾個操作而已,不需要進行這樣的大動作。
所有的選擇不過是為了下一次選擇做準備