鑑於不少使用 ng-alain 都以 .net 爲後端,如下我將以一個示例來描述 ng-alain 如何同 .net core 一塊兒開發。示例以單箇中後臺項目爲基準,對於多項目的應用大致相同,但總體目錄結構固然不能以單個項目了,更多應該以多人開發爲準。html
全部源碼,從 Github 中獲取。
分爲 ng-alain 和 .net core 兩個部分,固然我推薦兩者分開建立在一個根目錄下,例如以一個 asdf
爲項目名,建立一個 asdf
目錄,而後分別使用如下方式構建一個完整的先後端:前端
md asdf cd asdf
ng-alaingit
ng new asdf --style less cd asdf ng add ng-alain
此時,咱們將 asdf/asdf
目錄重命名另外一個名稱以便區分先後端:fe
。github
.net coreweb
dotnet new webapi -o asdf # 這裏採用命令行,依然可使用 vs 建立項目
一樣,咱們將 asdf/asdf
目錄重命名另外一個名稱以便區分先後端:be
。npm
示例的命名方式你可使用你喜歡的風格。
最終咱們的 asdf
項目的目錄結構以下:json
asdf - be (後端 .net core) - fe (前端 Angular)
先後端分開天然,分別開啓兩個命令行,而 vscode 着實很方便,分別在 be
和 fe
目錄下運行:後端
be: dotnet watch run fe: npm run hmr
默認狀況下分開能夠經過 https://localhost:5001/api/values 和 http://localhost:4200/ 訪問先後端。api
IIS跨域
其實如果使用 vs 建立項目,能夠更友好的將後端綁定至某個域名下,配合修改 host 可使開發環境更接近生產環境。固然啦,這一部分百度或博客園能夠獲得信息支持,這裏再也不贅述。
我在淺談Angular網絡請求 描述過網絡請求與用戶認證相關的,以此爲擴展,咱們來嘗試怎麼實現這些細節。
.net core 也有中間件的概念,如同 Angular 攔截器。依然以一個網絡請求流程式來描述這一過程,其大概以下:
固然,這只是一個大概性,細節上可能須要處理得更多。
在 Startup.cs
增長一個簡單經過 header
來獲取 token
屬性值,並進行校驗有效性,最後再結果寫入至 context.Items
裏,這樣整個請求只須要一次用戶信息,後續直接使用 context.Items
來獲取用戶信息。
app.Use(async (context, next) => { if (!context.Request.Path.ToString().StartsWith("/api/passport")) { var _token = ""; if (context.Request.Headers.TryGetValue("token", out var tokens) && tokens.Count > 0) { _token = tokens[0]; } if (_token != "asdf") { context.Response.StatusCode = 401; return ; } var user = new User(); user.Id = 1; user.Name = "cipchk"; context.Items.Add("token", _token); context.Items.Add("user", user); } await next(); });
整段代碼實現幾個細節:
/api/passport
開頭用戶 Token 校驗從 headers 獲取 token
值,並校驗值
401
再也不執行後續動做User
信息並寫入 context.Items
中,之後後續請求直接讀取此時,若再一次訪問 https://localhost:5001/api/values 後端接收到的是一個 401
狀態碼。
依然可使用中間件的寫法,但爲了區分不一樣,這裏採用過濾器的寫法。
繼承 ExceptionFilterAttribute
並重寫 OnException
就能夠簡單的完成,ExceptionFilterAttribute
異常過濾器會在執行過程當中若遇到 throw
時會被觸發。
public class ExceptionAttribute : ExceptionFilterAttribute { public override void OnException(ExceptionContext context) { var res = context.HttpContext.Response; res.StatusCode = 200; res.ContentType = "application/json; charset=utf-8"; context.Result = new JsonResult(new { msg = context.Exception.Message, code = 503 }); } }
最後,須要註冊到整個應用裏。
services.AddMvc(options => { options.Filters.Add(new ExceptionAttribute()); })
新建一個 PassportController.cs
文件,內容以下:
[Route("api/[controller]")] [ApiController] public class PassportController : ControllerBase { [HttpGet("{id}")] public JsonResult Get(int id) { if (id != 1) throw new Exception("無效用戶"); return new JsonResult(new { msg = "ok", data = "asdf" }); } }
前面咱們已經忽略對全部 /api/passport
開頭的 URL 的用戶 Token 校驗,它是一個受權頁理當如此。這裏體現了兩個細節:
若無效用戶拋出一個錯誤
ExceptionAttribute
會捕獲到這個錯誤,並從新指定變動響應體內容return new JsonResult(new { msg = "無效用戶" });
這種方式這裏都是手工建立統一響應體,你依然能夠利用過濾器或中間件來統一處理響應體爲統一風格,而對於方法內永遠都只返回一個 data
對應值。上述單純只是一個示例,須要自行更進一步封裝。
本文描述是先後端分開開發,所以開發過程當中勢必存在跨域請求的問題,一個簡單的辦法在 Startup.cs
裏增長跨域代碼:
#if DEBUG app.UseCors(builder => { builder.AllowAnyMethod() .AllowAnyHeader() .AllowAnyOrigin(); }); #endif
條件編譯能夠很好的解決生產和開發環境的不一樣,由於部署時咱們將不存在跨域問題,後續會描述。
到此,咱們已經實現大部分 淺談Angular網絡請求 描述的功能,哪怕都是一個簡化,但咱們能夠訪問:
ng-alain 默認是以儘量接收生產環境項目的腳手架,誠如我在 淺談Angular網絡請求 描述的那樣,不該該編寫一個簡單的 Hello World 請求來校驗是否可用。
在開始以前,須要先了解 Angular 環境變動配置,它位於 fe/src/environments
目錄中,每個文件表示一種環境,他們有者相同參數,但其值可能各不一樣。
如果你跟着本文來作的話,那麼相對應的是 environment.hmr.ts
,咱們修改這裏的 SERVER_URL
值爲 https://localhost:5001/api/
。這是表示全部請求都會在請求URL前自動加上該地址。
Angular 啓用前咱們能作的事只有這裏,腳手架默認實現了 StartupService
(位於:fe/src/app/core/startup/
下),固然默認代碼並不可用,咱們將其修改成:
load(): Promise<any> { // only works with promises // https://github.com/angular/angular/issues/15088 return new Promise((resolve, reject) => { this.httpClient.get('values').subscribe( (res: any) => { this.injector.get(NzMessageService).success(JSON.stringify(res)); }, () => {}, () => { resolve(null); }, ); }); }
這裏請求是 api/values
,但因爲咱們給 Angular 環境變量統一配置 URL 前綴,所以只須要一個簡單的 values
爲請求 URL。(注:若請求URL地址不是指望結果,須要從新運行 npm run hmr
)
此時,你訪問前端時會自動跳轉至 /passport/login
登陸頁,這一切都是因爲 @delon/auth
用戶認證模塊在管理的,咱們沒有寫任何一行關於前端校驗的代碼。
登陸示例頁的大部分代碼是可用的,但本文並不關心這一些,咱們修改其中發送請求部分以下:
// mock http this.loading = true; this.http.get('passport/1').subscribe((res: any) => { this.loading = false; // 清空路由複用信息 this.reuseTabService.clear(); // 設置Token信息 this.tokenService.set({ token: res.data, }); // 從新獲取 StartupService 內容,若其包括 User 有關的信息的話 this.startupSrv.load().then(() => this.router.navigate(['/'])); // 不然直接跳轉 // this.router.navigate(['/']); });
請求 passport/1
返回用戶 Token,並把 Token 值寫入 TokenService
中,最後跳轉至儀表盤頁。
登陸成功後,你還會接收到一個條 [ "value1", "value2" ]
的消息,這是來自 APP_INITIALIZER
產生的,至少代表咱們能正常訪問到 values
,由於此時你會發現該主體的 Header
已經包含了 token: asdf
字樣。
關於前端部分我簡略的描述,這裏還有更多細節,例如:響應體直接返回 data
的內容,更多細節自行挖掘。
示例中的 .net core 部分是單純的 Web API 項目,由此不存在任何頁面之說。而如何讓 .net core 項目直接以 Angular 項目來訪問呢?這須要解決兩個問題。
wwwroot
目錄裏index.html
默認訪問頁這裏一個簡單的辦法是修改 angular.json
的輸出路徑(固然也能夠直接命令行裏直接指定):
"outputPath": "../be/wwwroot",
最後,執行:
npm run bash
打包後的文件會直接存放至 be/wwwroot
目錄下。
默認狀況下須要開啓靜態資源訪問能力而且指定默認頁爲 index.html
,在 Startup.cs
增長:
var options = new DefaultFilesOptions(); options.DefaultFileNames.Clear(); options.DefaultFileNames.Add("index.html"); app.UseDefaultFiles(options); app.UseStaticFiles();
此時,若你再訪問 https://localhost:5001/ 會發現咱們啓動的是一個 ng-alain 項目。
而對於後端的打包,也很是簡單:
dotnet publish -o ../dist
最終,直接將根目錄下的 dist
部署至 Web 服務器上。
本文算是對 淺談Angular網絡請求 進一步實踐,雖然一切都採用簡化的代碼來解釋,但整體的流程是等同的。
基於 .net core 爲後端是出於羣裏反應不少項目都是使用 .net,多是 .net framework 版本,但本質上是相同。示例我是以 vscode 編寫,可能後端的格式會有些同 vs 不一樣。
(完)