承接上一篇文章,接下來會說些非常簡單的例子,也比較符合 Restful API 的規(guī)范,并且會選用 .NET Core 作為后臺開發(fā)平臺柱彻,使用 C# 作為開發(fā)語言,.NET Core 目前最新版本是 2.1 餐胀,很多人質疑 .NET 和 C#哟楷,覺得太老了,市場上已經沒有什么占有率了否灾,學校也不怎么交了卖擅,工作也不怎么好找了,被 Java 吊著打之類的墨技。在網(wǎng)絡中惩阶,或是在工作中,我使用的技術椏弁簦總是會被各種人 Diss断楷,這其中有使用 Java的,有使用 Python 的崭别,有使用 Go Lang 的冬筒,有使用 Ruby 的恐锣,甚至也有使用 Nodejs 的。每一種語言或平臺或 Framework 都有它的不足舞痰,覺得自己順手的才是最好的土榴。質疑 .NET 也完全沒必要,在企業(yè)應用中响牛,.NET 還占據(jù)很大的市場玷禽,甚至騰訊的支付清算網(wǎng)關也已經使用了 .NET Core 重寫了,如果現(xiàn)在開始學習 .NET 的話呀打,我覺得從 .NET Core 會是一個絕好的開端论衍。并且在我上家公司中,我建議了公司采用了 .NET Core + Nancy + SQLServer 完全重寫了服務后端和數(shù)據(jù)分析平臺聚磺,并且服務也是運行在 Cent OS 上,穩(wěn)定性也非常高炬丸。
.NET Core 入門非常簡單瘫寝,簡單到什么程序呢,我覺得任何使用過 Nodejs 和 Python 的都能上手稠炬,官網(wǎng)地址是:
https://www.microsoft.com/net/learn/get-started-with-dotnet-tutorial
開發(fā)工具也非常豐富焕阿,假如以前是用 Notepad++ 或 Sublime 寫 PHP 的碼農,可以使用 Vscode 首启,這是官方指定的編輯器暮屡,或者繼續(xù)使用 Notepad+。如果是 .NET 老碼農毅桃,可以繼續(xù)使用宇宙第一的 IDE Visual Studio 2018 community褒纲。如果是 Java 或者 Android 或者習慣腦漿噴射全家桶 (Jetbrains) 的碼農,可以使用 Rider钥飞,所以無論你以前使用什么語言莺掠,都能找到順手的工具。
這里读宙,我使用 Rider彻秆,因為和 Android Sutdio 界面基本相同,都是 Jetbrains 家的结闸,并確保你已經安裝了最新的 .NET Core唇兑,安裝完成后,使用 dotnet --version 查看一下版本號桦锄,并且我會使用 Fancy 作為 Web 框架扎附,而不是使用 .NET Core ,這有一個好處结耀,就是可以在 Windows帕棉、Linux针肥、Mac OS 下都可以運行,并且不論是 IIS 還是 Owin 承載方式都能運行起來香伴,甚至丟在一個虛擬主機上也能完整的運行起來慰枕。我之前寫了一個輕博客系統(tǒng)用來同步我的簡書文章,就是使用 Nancy 作為框架即纲,支持任何 .NET 運行環(huán)境和服務器軟件具帮,甚至可以生成靜態(tài)頁面,支持 GitPages 和任何靜態(tài)頁面服務低斋。
讓我們先從一個學生管理系統(tǒng)說起蜂厅,這也是大多數(shù)例子常用的場景,首先創(chuàng)建一個 Solution膊畴,類型為 .NET Core Console Application掘猿。確保右側的選項卡中,Language 為 C#唇跨,F(xiàn)ramework 為 netcoreapp2.x稠通。
之后我們會在解決方案資源管理器中看見以下工程結構:
Program.cs 就是整個工程的入口點,也是 Main 方法所在的類买猖,就像是 MainActivity 一樣(雖然不太恰當)改橘,可以看到右側編輯器中代碼內容如下:
using System;
namespace StudentManagementSystem
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
點擊工具欄上的綠色三角形就可以啟動我們的第一個 .NET Core Console Application 了,旁邊的小蟲子圖標就是 Debug玉控,搞過 Android 開發(fā)用過 Android Studio 的都知道飞主。
之后會彈出下面的對話框,讓你做第一次運行配置高诺,如果不知道下面的每一項代表什么意思碌识,那就直接點擊 Run 就可以。
然后就可以在狀態(tài)窗口中看到結果輸出:
如果能正常進行到這一步虱而,我覺得是時候配置 Web 容器(框架)了丸冕,就像 Jetty 或者 Springboot 里的內置 Tomcat 一樣,它不直接處理業(yè)務邏輯薛窥,而是處理 HTTP 請求胖烛,路由等,然后由 Nancy 根據(jù)對應的路由來處理對應的業(yè)務诅迷,最終發(fā)布自身可以完成端口注冊監(jiān)聽佩番,不過在實際生產環(huán)境中都會使用反向代理服務器。
好的罢杉,扯遠了趟畏,在 .NET Core 中,我建議使用 Kestrel 作為 Web 容器服務器滩租,項目地址:
KestrelHttpServer: https://github.com/aspnet/KestrelHttpServer
Kestrel 是微軟開發(fā)的一個跨平臺的 Web 容器服務器赋秀,它基于 libuv 這個網(wǎng)絡庫開發(fā)的利朵。libuv 是一個抽象層,它在 Linux 上是由 libev 實現(xiàn)猎莲,在 Windows 上是由 IOCP 實現(xiàn)绍弟。
要引用第三方庫,我們使用包管理器 Nuget著洼,切換到 Nuget 任務窗格樟遣。
然后搜索 Kestrel 關鍵字,選擇 Microsoft.AspNetCore.Server.Kestrel 并在右側窗格中加入到項目中引用身笤。
同理依次添加下列第三方庫的引用:
Microsoft.AspNetCore.Hosting
Microsoft.AspNetCore.Owin
Microsoft.Extensions.Configuration.Binder
Microsoft.Extensions.Configuration.Json
Nancy
然后豹悬,在項目中創(chuàng)建一個名為 Startup 的類,Startup 是一個每個 Owin 程序都會有的一個類液荸,Owin 是一組應用服務器承載規(guī)范瞻佛,理論上 Startup 需要實現(xiàn)接口 IStartup,不過在 .NET Core 中娇钱,都沒有明確表示必須使用接口實現(xiàn)伤柄,而是被反射調用。
public interface IStartup
{
IServiceProvider ConfigureServices(IServiceCollection services);
void Configure(IApplicationBuilder app);
}
所以我們創(chuàng)建一個 Startup 類忍弛,指定應用程序管道模型將會由 Nancy 進行處理。
namespace StudentManagementSystem
{
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Nancy.Owin;
public class Startup
{
public Startup(IHostingEnvironment env)
{
}
public void Configure(IApplicationBuilder app)
{
app.UseOwin(x => x.UseNancy());
}
}
}
接下來創(chuàng)建一個 Nancy 處理模塊考抄,名為 HomeModule细疚,如果對 ASP.NET MVC 或者 Java Servlet 有所了解的朋友應該了解這么個意思,它用來處理對應的 URL 的 HTTP 請求川梅,可以根據(jù)不同的 HTTP 謂詞返回不同的結果疯兼。
所有的 Module 必須繼承于它的父類 NancyModule,并在默認構造函數(shù)里處理請求贫途,這用起來雖然有點怪吧彪,不過沒關系,用習慣了就會一發(fā)不可收拾丢早。
所有的謂詞都是可以通過 NancyModule 中提供的虛方法自行實現(xiàn)姨裸,例如 GET 請求,其源代碼如下:
/// <summary>
/// Declares a route for GET requests.
/// </summary>
/// <param name="path">The path that the route will respond to</param>
/// <param name="action">Action that will be invoked when the route it hit</param>
/// <param name="name">Name of the route</param>
/// <param name="condition">A condition to determine if the route can be hit</param>
public virtual void Get(string path, Func<dynamic, object> action, Func<NancyContext, bool> condition = null, string name = null)
{
this.Get<object>(path, action, condition, name);
}
/// <summary>
/// Declares a route for GET requests.
/// </summary>
/// <typeparam name="T">The return type of the <paramref name="action"/></typeparam>
/// <param name="path">The path that the route will respond to</param>
/// <param name="action">Action that will be invoked when the route it hit</param>
/// <param name="name">Name of the route</param>
/// <param name="condition">A condition to determine if the route can be hit</param>
public virtual void Get<T>(string path, Func<dynamic, T> action, Func<NancyContext, bool> condition = null, string name = null)
{
this.Get(path, args => Task.FromResult(action((DynamicDictionary)args)), condition, name);
}
/// <summary>
/// Declares a route for GET requests.
/// </summary>
/// <param name="path">The path that the route will respond to</param>
/// <param name="action">Action that will be invoked when the route it hit</param>
/// <param name="name">Name of the route</param>
/// <param name="condition">A condition to determine if the route can be hit</param>
public virtual void Get(string path, Func<dynamic, Task<object>> action, Func<NancyContext, bool> condition = null, string name = null)
{
this.Get<object>(path, action, condition, name);
}
/// <summary>
/// Declares a route for GET requests.
/// </summary>
/// <typeparam name="T">The return type of the <paramref name="action"/></typeparam>
/// <param name="path">The path that the route will respond to</param>
/// <param name="action">Action that will be invoked when the route it hit</param>
/// <param name="name">Name of the route</param>
/// <param name="condition">A condition to determine if the route can be hit</param>
public virtual void Get<T>(string path, Func<dynamic, Task<T>> action, Func<NancyContext, bool> condition = null, string name = null)
{
this.Get(path, (args, ct) => action((DynamicDictionary)args), condition, name);
}
/// <summary>
/// Declares a route for GET requests.
/// </summary>
/// <param name="path">The path that the route will respond to</param>
/// <param name="action">Action that will be invoked when the route it hit</param>
/// <param name="name">Name of the route</param>
/// <param name="condition">A condition to determine if the route can be hit</param>
public virtual void Get(string path, Func<dynamic, CancellationToken, Task<object>> action, Func<NancyContext, bool> condition = null, string name = null)
{
this.Get<object>(path, action, condition, name);
}
/// <summary>
/// Declares a route for GET requests.
/// </summary>
/// <typeparam name="T">The return type of the <paramref name="action"/></typeparam>
/// <param name="path">The path that the route will respond to</param>
/// <param name="action">Action that will be invoked when the route it hit</param>
/// <param name="name">Name of the route</param>
/// <param name="condition">A condition to determine if the route can be hit</param>
public virtual void Get<T>(string path, Func<dynamic, CancellationToken, Task<T>> action, Func<NancyContext, bool> condition = null, string name = null)
{
this.AddRoute("GET", path, action, condition, name);
}
我們可以實現(xiàn)一個處理 GET 路由請求 /api/hello 的處理方法怨酝,如下:
public class HomeModule:NancyModule
{
public HomeModule()
{
Get("/api/hello", _ => { return "hello nancy!"; });
}
}
現(xiàn)在是時候檢驗一下我們的這個路由是否生效傀缩,并且是否能按照我們預期處理這個 GET 請求,還需要做一件事农猬,就是在 Main 方法里啟動 Kestrel赡艰,并制定啟動管道配置和處理類為 Startup。
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseKestrel()
.UseStartup<Startup>()
.Build();
host.Run();
添加完這部分代碼之后斤葱,編譯執(zhí)行慷垮,看看 Run 窗口有沒有下面的輸出:
如果由揖闸,恭喜你你的第一個 .NET Core 接口服務已經成功 Run 起來了,此時可以點擊這個超鏈接 http://localhost:5000 看看瀏覽器有什么料身,不出意外汤纸,會出現(xiàn)一個 404 頁面,這是因為我們沒有為根請求進行任何處理惯驼,而此時只要輸入我們的第一個 GET 請求的 URL http://localhost:5000/api/hello 的時候蹲嚣,就會出現(xiàn)下面的頁面,赫然已經看到了我們的代碼已經生效祟牲,這個 GET 請求已經被成功處理了隙畜。
或者,我們嚴謹一定啊说贝,讓這個接口返回 JSON 數(shù)據(jù)议惰,畢竟 APP 請求接口,還是 JSON 處理的比較方便乡恕。
public HomeModule()
{
Get("/api/hello", _ =>
{
return this.Response.AsJson(new
{
result = 0,
message = "ok"
});
});
}
再次執(zhí)行查看輸出言询,即可看到我們已經成功輸出了 JSON 類型的數(shù)據(jù)了。
OK傲宜,從零開始就到這里了运杭,感謝各位看官!