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));