1. 程式人生 > >將app接口服務器改為dotnet core承載

將app接口服務器改為dotnet core承載

clas 我不 .com linu strac dot static 需要 spa

昨天我的一個 app 的接口服務器掛掉了,國外的小雞意外的翻車,連同程序和數據一起,猝不及防。我的服務端程序是 asp.net mvc ,小雞是 256 M 的內存跑不了 windows 系統,裝的 mono 。服務器用的 jexus,但是還有一個 apache+php+mysql 的全家桶占用了 80 端口,所以這個接口是通過 apache 反向代理的。

這樣一來本來環境就很復雜了,我 ubuntu 16.04 裝 mono 下載了差不多700 mb 的數據,安裝後體積更大,簡直太不環保了,只有不到 10G 的硬盤。於是狠下心將服務器端程序重寫,其它快餐語言我不會,據說 nodejs 和 python 會很快,部署也方便。但我還是用我的大 C#,好在現在有 dotnet core 了,也給大家安利一發,它是一個模塊化的開發棧,也是未來的所有.NET平臺的基礎,橫跨 Windows、Linux、OSX 三大主流系統。

因為我的接口比較簡單,主要是輸出 json 以及幾個靜態頁面。所以不需要創建 web 項目,我並不想讓他寄宿在服務器軟件上運行,自己實現 Http 監聽處理請求即可,不過這些 dotnet core 已經為你準備好了一個 Server.Kestrel,不需要自己造輪子。

關於 Server.Kestrel 可以參考這篇文章 ,更多的還是官方更詳細,傳送門 ,以及源碼和示例:https://github.com/aspnet/KestrelHttpServer

在包管理控制臺執行安裝:

PM>  Install-Package Microsoft.AspNetCore.Server.Kestrel -Pre

另外,如果需要靜態文件支持,還需要下面的庫:

PM>  Install-Package Microsoft.AspNetCore.StaticFiles -Pre

使用很簡單,在 Main 方法裏實例化一個 WebHostBuilder 並調用 run 方法就可以,其他的都是配置。

var host = new WebHostBuilder()
    .UseKestrel()
    .UseUrls("http://*:5001")
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseStartup<Program>()
    .Build();
host.Run();

處理請求簡直不要太簡單:

app.Run(async (context) =>
{
    byte[] data = Encoding.UTF8.GetBytes("hello world");
    await context.Response.Body.WriteAsync(data, 0, data.Length).ConfigureAwait(false);
});

但是顯然不夠強大,無法處理 url 路由,接下來寫一個抽象類處理 http 請求。

abstract class HandlerBase
{
    public abstract void Process(HttpContext context);
}

這裏可以用一個 Dictionary<string,Handler> 保存路由:

_routes = new Dictionary<string, HandlerBase>();
_routes.Add("/home/hello", new Hello());
_routes.Add("/test/demo", new Demo());

Hello 這個類需要繼承 HandlerBase 抽象類,重寫 Process 方法:

class Hello : HandlerBase
{
    public async override void Process(HttpContext context)
    {
        byte[] data = Encoding.UTF8.GetBytes("hello world");
        await context.Response.Body.WriteAsync(data, 0, data.Length).ConfigureAwait(false);
    }
}

這樣就避免了為了處理路由寫一堆 if else,擴展性也比較好,根據 url 路徑找到對應的 HandlerBase 的實現,並調用 Process 處理請求。

app.Run(async (context) =>
{
    HandlerBase handler = null;
    _routes.TryGetValue(context.Request.Path.ToString().ToLower(), out handler);
    if (handler != null) handler.Process(context);
    else
    {
        byte[] data = Encoding.UTF8.GetBytes("HTTP 404");
        await context.Response.Body.WriteAsync(data, 0, data.Length).ConfigureAwait(false);
    }
});

瀏覽器打開效果 技術分享

然後就是靜態文件的處理問題,建議放一個文件夾存放靜態文件,比如創建 dotnet core web 程序時,會有一個 www 的文件夾。

Kestrel 處理靜態內容也很簡單:

app.UseStaticFiles(new StaticFileOptions()
{
    FileProvider = _fileProvider,
    RequestPath = ""
    
});

FileProvider 是必須是實現了 IFileProvider 的類。

IFileProvider _fileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "www"));

由於 RequestPath 是空字符串,這樣一來只要訪問 /abc.txt 就會直接映射到 www 目錄下的 abc.txt 文件並原始返回。

發布項目後會產生一個 PublishOutput 文件夾,將裏面的內容復制到主機 /home/test 目錄中。要運行這個項目還需要在服務器安裝 dotnet core ,這並不需要再原代碼重新編譯了,怎麽安裝可以參考官網。

執行下面命令運行你的項目,如果你的項目叫 demo ??:

dotnet demo.dll

啟動程序

技術分享

最後的最後,如果想深入學習,不要只是執行個 Hello World,絕知此事要躬行!

將app接口服務器改為dotnet core承載