前言
一天天不知道怎么過的,但確實挺忙,事趕事不帶停那種,讓我感覺跟在流水線干活一樣,忙活的事差不多了就喘口氣繼續補充這一系列的內容,前面幾篇基本上把一個常規的后端服務搭建差不多了,后面的會時不時根據自己需要或者常規的測試內容來一點點完善更新。
攔截器
這里先不提AOP的內容,其實在我個人之前的理解,AOP開發的思想就是,我們做的某些操作例如身份驗證,日志記錄,異常抓捕等等這些操作,可以單獨拎出來放那,誰用了加個頭部標識就可以了,剩余的交給代碼來處理,這樣我們開發就只需要關心業務功能,而其他的全都可以不用考慮,這就是框架的好處,別人封裝集成好,就可以省去很大的開發工作量。
好,開始說攔截器,其實也是中間層,個人感覺跟AOP的概念類似,就放到這里寫上了。
異常攔截器
在我們Api的工程目錄下新建文件夾Filters用於存放攔截器,之后我們新建ExceptionFilter這個異常的攔截器,用於記錄工程拋異常並做對應回調處理。
代碼如下,具體不過多解釋,因為實在覺得這個沒啥說的,只是注意異步調用的問題即可。
public class ExceptionFilter
{
private readonly RequestDelegate _next;
/// <summary>
///
/// </summary>
/// <param name="next"></param>
public ExceptionFilter(RequestDelegate next)
{
_next = next;
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex) //發生異常
{
context.Response.StatusCode = 500;
LogUtil.Error($"response exception:{ex.Message}");// {ex.StackTrace}
await ResponseUtil.HandleExceptionAsync(500, "服務器錯誤");
}
}
}
這個地方的ResponseUtil是單獨在Util層創建的(公共類盡量扔到同一個工程類庫下,以后一鍵打包,各種復用)。
public class ResponseUtil
{
/// <summary>
/// 回調
/// </summary>
/// <param name="statusCode">html狀態碼</param>
/// <param name="msg">消息</param>
/// <returns></returns>
public static Task HandleExceptionAsync(int statusCode, string msg)
{
var data = new { code = statusCode, msg = msg };
string text = JsonConvert.SerializeObject(data);
var response = AprilConfig.HttpCurrent.Response;
if (string.IsNullOrEmpty(response.ContentType))
{
//跨域的時候注意,不帶header沒法接收回調
response.Headers.Add("Access-Control-Allow-Origin", "*");
response.Headers.Add("Access-Control-Allow-Credentials", "true");
//因為這個是json
response.ContentType = "application/json;charset=utf-8";
response.StatusCode = 200;
response.ContentLength = text.Length;
return response.WriteAsync(text);
}
else
{
return response.WriteAsync(text);
}
}
}
之后我們依然要在Startup中注冊我們這個中間層。
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware<ExceptionFilter>();
…
}
這樣我們在全局如果出現異常的時候,可以統一捕獲到問題,然后做記錄,當然在測試環境下注意,如果這個錯誤幫助頁打開的時候,那上面的攔截器將毫無亂用。
測試結果
這樣如果真是哪個地方沒有做異常捕獲,全局最終都會一個不漏的抓住然后告訴你,好處是如果懶那就所有地方都不寫了,問題是有些異常即使捕獲但是不需要告知用戶也不需要做記錄(比如文件上傳下載的線程中斷異常之類的),所以這個只是為了保險起見而不是為了省事。
身份驗證攔截器
接下來我們繼續創建一個AuthFilter,目的是做身份驗證的判斷,如果沒通過就沒必要再進入具體的控制器了。
public class AuthFilter
{
private readonly RequestDelegate _next;
public AuthFilter(RequestDelegate next)
{
_next = next;
}
public Task Invoke(HttpContext context)
{
if (context.Request.Method == "OPTIONS")
{
return _next(context);
}
var headers = context.Request.Headers;
//檢查頭文件是否有jwt token
if (!headers.ContainsKey("Authorization"))
{
string path = context.Request.Path.Value;
if (!AprilConfig.AllowUrl.Contains(path) && path.IndexOf("swagger") < 0)
{
//這里做下相關的身份校驗
return ResponseUtil.HandleExceptionAsync(401, "請登錄");
//判斷是否有權限查看(在身份驗證后判斷對應的權限,這個方法后續再寫)
return ResponseUtil.HandleExceptionAsync(-2, "無權訪問");
}
}
return _next(context);
}
}
同樣我們需要在Startup注冊使用中間層。
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMiddleware<ExceptionFilter>();
app.UseMiddleware<AuthFilter>();
…
}
測試
然后訪問我們的Swagger,效果就很明顯了。
小結
這一篇主要就是引入中間層的使用,個人認為什么AOP開發OOP開發完全因人而異,沒必要為了追求新技術而去整體大功能改造,新技術確實使用起來方便,也有很好的前景,但是對於企業來講,穩定是最重要的,不會為了1%的性能速度而去冒30%甚至更高的風險,但是我還是要說一句,net core到目前為止已經歷過一個大版本的更新,雖然3.0沒有正式發布,但是一個個版本的更新之后,穩定性已經很ok了,所以該吃螃蟹都可以動手了。
下一篇,繼續引入AOP的開發,主要用的第三方的組件AspectCore,將針對接口調用的時候做一些常規操作。
補充 2019-07-31
今天在做調試的時候發現一個問題,現狀如下
發現這個問題我的第一反應是,字符編碼,但是看到我回調的時候明顯已經設置了ContentType,所以這個應該不是錯誤的原因,但是多次刷新的測試結果是偶爾正常,怪異的情況總是伴隨着bug,於是比對了正確的回調信息和錯誤的回調信息(這里是通過chrome瀏覽器調試然后獲取的回調信息,具體調試方法后續前端介紹,當然基本上都知道)。
這樣一看發現了問題所在,所以這個地方決定不再自主去設置Length了。