ABP throw UserFriendlyException() ,會讓前端彈出界面。
實現的話,首先 throw UserFriendlyException() 並不會在后端產生真正的異常,否則就是500 錯誤了。
ABP 的默認實現,是所有的請求,包裹在result { },里面,這個是ABP 封裝的(可以去掉)
這個是怎么在前端解析出來實際的響應內容呢? 前端有 abpHttpInterceptor.js 這在前端啟動的時候注冊的一個HttpInterceptor。
里面有這個方法,來解析出來實際的 response. 這個Interceptor 會在發送請求的時候攔截,響應回來的時候攔截,也相當於一節管道了。
1 AbpHttpConfiguration.prototype.handleAbpResponse = function (response, ajaxResponse) { 2 var newResponse; 3 if (ajaxResponse.success) { 4 newResponse = response.clone({ 5 body: ajaxResponse.result 6 }); 7 if (ajaxResponse.targetUrl) { 8 this.handleTargetUrl(ajaxResponse.targetUrl); 9 ; 10 } 11 } 12 else { 13 newResponse = response.clone({ 14 body: ajaxResponse.result 15 }); 16 if (!ajaxResponse.error) { 17 ajaxResponse.error = this.defaultError; 18 } 19 this.logError(ajaxResponse.error); 20 this.showError(ajaxResponse.error); 21 if (response.status === 401) { 22 this.handleUnAuthorizedRequest(null, ajaxResponse.targetUrl); 23 } 24 } 25 return newResponse; 26 };
再來看后端怎么實現的。
后端有一個AbpMvcAuditFilter.cs 他的 一部分代碼在這里。通過這兒,可以看出這里的Filter把 filterContext.Result 進行改動。我們也可以在這里加上一層。
這里是Audit 的部分,原理類似。
if (_auditingConfiguration.SaveReturnValues && filterContext.Result != null) { switch (filterContext.Result) { case AbpJsonResult abpJsonResult: if (abpJsonResult.Data is AjaxResponse ajaxResponse) { auditData.AuditInfo.ReturnValue = _auditSerializer.Serialize(ajaxResponse.Result); } else { auditData.AuditInfo.ReturnValue = _auditSerializer.Serialize(abpJsonResult.Data); } break; case JsonResult jsonResult: auditData.AuditInfo.ReturnValue = _auditSerializer.Serialize(jsonResult.Data); break; case ContentResult contentResult: auditData.AuditInfo.ReturnValue = contentResult.Content; break; } }
在 .Netcore api 中生成的 swagger 文檔中,前端Angular 在攔截器中添加如下 處理
return next.handle(req1).pipe(tap(() => { }, (err: any) => { if (err instanceof HttpErrorResponse) { if (err.status !== 401) { console.error(err.message); return; } this.router.navigate(['login']); } }));
handle(req),執行的就是請求后端。 這里的返回是一個Observable 對象。可以pipe或者 subscribe().
pipe里可以有一些 operator. 也就是一些方法。 scan()是一個累加器,會接受一個一個的值,然后把這些值一個一個的算出一個結果來,也就是會記住歷史,累計歷史。
他的初始值是一個參數:seed.
https://stackblitz.com/edit/dcm2d1?file=index.ts
下面的代碼,說明了merge和contact的差別
import { merge, interval, concat } from 'rxjs'; import { take, tap } from 'rxjs/operators'; const timer1 = interval(1000).pipe(tap(t=>console.log('timer1')),take(3)); const timer2 = interval(2000).pipe(tap(t=>console.log('timer2'))); const timer3 = interval(500).pipe(tap(t=>console.log('timer3'))); const concurrent = 2; // the argument const merged = merge(timer1, timer2, timer3); // 可以替換為 contact 看看 merged.subscribe(x => console.log(x));