|
@@ -0,0 +1,272 @@
|
|
|
+import {
|
|
|
+ HttpErrorResponse,
|
|
|
+ HttpEvent,
|
|
|
+ HttpHandler,
|
|
|
+ HttpHeaders,
|
|
|
+ HttpInterceptor,
|
|
|
+ HttpRequest,
|
|
|
+ HttpResponse,
|
|
|
+ HttpResponseBase
|
|
|
+} from '@angular/common/http';
|
|
|
+import { Injectable, Injector } from '@angular/core';
|
|
|
+import { Router } from '@angular/router';
|
|
|
+import { DA_SERVICE_TOKEN, ITokenService } from '@delon/auth';
|
|
|
+import { CacheService } from '@delon/cache';
|
|
|
+import { _HttpClient, MenuService } from '@delon/theme';
|
|
|
+import { deepCopy } from '@delon/util';
|
|
|
+import { environment } from '@env/environment';
|
|
|
+import { NzNotificationService } from 'ng-zorro-antd/notification';
|
|
|
+import { Observable, of, throwError } from 'rxjs';
|
|
|
+import { catchError, mergeMap } from 'rxjs/operators';
|
|
|
+
|
|
|
+import * as incontrolUtils from '../../shared/utils/ic-utils';
|
|
|
+import { GeneralUtilsService } from '../utils/general-utils.service';
|
|
|
+
|
|
|
+const CODEMESSAGE = {
|
|
|
+ 200: '服务器成功返回请求的数据。',
|
|
|
+ 201: '新建或修改数据成功。',
|
|
|
+ 202: '一个请求已经进入后台排队(异步任务)。',
|
|
|
+ 204: '删除数据成功。',
|
|
|
+ 400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
|
|
|
+ 401: '用户没有权限(令牌、用户名、密码错误)。',
|
|
|
+ 403: '用户得到授权,但是访问是被禁止的。',
|
|
|
+ 404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
|
|
|
+ 406: '请求的格式不可得。',
|
|
|
+ 410: '请求的资源被永久删除,且不会再得到的。',
|
|
|
+ 422: '当创建一个对象时,发生一个验证错误。',
|
|
|
+ 500: '服务器发生错误,请检查服务器。',
|
|
|
+ 502: '网关错误。',
|
|
|
+ 503: '服务不可用,服务器暂时过载或维护。',
|
|
|
+ 504: '网关超时。',
|
|
|
+ 651: '证书无效,需要申请'
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 默认HTTP拦截器,其注册细节见 `app.module.ts`
|
|
|
+ */
|
|
|
+@Injectable()
|
|
|
+export class DefaultInterceptor implements HttpInterceptor {
|
|
|
+ notify401: any;
|
|
|
+ icUtils = incontrolUtils;
|
|
|
+ constructor(private injector: Injector, public srv: CacheService, private menuService: MenuService) {}
|
|
|
+
|
|
|
+ private get notification(): NzNotificationService {
|
|
|
+ return this.injector.get(NzNotificationService);
|
|
|
+ }
|
|
|
+
|
|
|
+ private goTo(url: string) {
|
|
|
+ setTimeout(() => this.injector.get(Router).navigateByUrl(url));
|
|
|
+ }
|
|
|
+
|
|
|
+ private utils(): GeneralUtilsService {
|
|
|
+ return this.injector.get(GeneralUtilsService);
|
|
|
+ }
|
|
|
+
|
|
|
+ private checkStatus(ev: HttpResponseBase) {
|
|
|
+ if ((ev.status >= 200 && ev.status < 300) || ev.status === 401) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // const errortext = CODEMESSAGE[ev.status] || ev.statusText;
|
|
|
+ // this.notification.error(`请求错误 ${ev.status}: ${ev.url}`, errortext);
|
|
|
+ }
|
|
|
+
|
|
|
+ private goToLogin() {
|
|
|
+ const currentTime = new Date().getTime();
|
|
|
+ if (!this.notify401 || (this.notify401 && currentTime - this.notify401 > 1000)) {
|
|
|
+ this.notification.error(`未登录或登录已过期,请重新登录。`, ``);
|
|
|
+ }
|
|
|
+ this.notify401 = new Date().getTime();
|
|
|
+ // 清空 token 信息
|
|
|
+ (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).clear();
|
|
|
+ // }
|
|
|
+ let initUrl = sessionStorage.getItem('ic_url_before_401');
|
|
|
+ if (!initUrl && !window.location.hash.includes('login')) {
|
|
|
+ initUrl = incontrolUtils.urlHash();
|
|
|
+ sessionStorage.setItem('ic_url_before_401', initUrl);
|
|
|
+ }
|
|
|
+ this.goTo('/passport/login');
|
|
|
+ }
|
|
|
+
|
|
|
+ private handleData(newReq: any, ev: HttpResponseBase): Observable<any> {
|
|
|
+ // 可能会因为 `throw` 导出无法执行 `_HttpClient` 的 `end()` 操作
|
|
|
+ this.checkStatus(ev);
|
|
|
+ // 业务处理:一些通用操作
|
|
|
+ switch (ev.status) {
|
|
|
+ case 200:
|
|
|
+ // 业务层级错误处理,以下是假定restful有一套统一输出格式(指不管成功与否都有相应的数据格式)情况下进行处理
|
|
|
+ // 例如响应内容:
|
|
|
+ // 错误内容:{ status: 1, msg: '非法参数' }
|
|
|
+ // 正确内容:{ status: 0, response: { } }
|
|
|
+ // 则以下代码片断可直接适用
|
|
|
+ // if (event instanceof HttpResponse) {
|
|
|
+ // const body: any = event.body;
|
|
|
+ // if (body && body.status !== 0) {
|
|
|
+ // this.msg.error(body.msg);
|
|
|
+ // /** 继续抛出错误中断后续所有 Pipe、subscribe 操作,因此: */
|
|
|
+ // /** this.http.get('/').subscribe() 并不会触发 */
|
|
|
+ // return throwError({});
|
|
|
+ // } else {
|
|
|
+ // /** 重新修改 `body` 内容为 `response` 内容,对于绝大多数场景已经无须再关心业务状态码 */
|
|
|
+ // return of(new HttpResponse(Object.assign(event, { body: body.response })));
|
|
|
+ // /** 或者依然保持完整的格式 */
|
|
|
+ // return of(event);
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ if (ev instanceof HttpResponse) {
|
|
|
+ const evBody = ev.body;
|
|
|
+ if (evBody.code && Number(evBody.code)) {
|
|
|
+ if (Number(evBody.code) === 692) {
|
|
|
+ this.notification.warning('导入失败', ev.body.msg, { nzDuration: 0 });
|
|
|
+ } else if ((Number(evBody.code) >= 600 && Number(evBody.code) < 700) || Number(evBody.code) === 500) {
|
|
|
+ let notInit = !(ev.url || '').includes('source=init');
|
|
|
+ if (notInit) {
|
|
|
+ this.notification.error(evBody.msg, '', { nzDuration: 3000 });
|
|
|
+ }
|
|
|
+ console.error('error', ev, newReq);
|
|
|
+ } else if (Number(evBody.code) >= 700 && Number(evBody.code) < 800) {
|
|
|
+ this.notification.success(evBody.msg, '', { nzDuration: 1500 });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (evBody.refreshToken) {
|
|
|
+ // console.log('refreshToken', evBody.refreshToken);
|
|
|
+ (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).set({
|
|
|
+ token: evBody.refreshToken
|
|
|
+ });
|
|
|
+
|
|
|
+ const token = (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).get()?.token;
|
|
|
+
|
|
|
+ // console.log('token', token);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case 401:
|
|
|
+ this.goToLogin();
|
|
|
+ break;
|
|
|
+ case 403:
|
|
|
+ this.goToLogin();
|
|
|
+ break;
|
|
|
+ case 404:
|
|
|
+ case 500:
|
|
|
+ // this.goTo(`/exception/${ev.status}`);
|
|
|
+ console.error('error', ev, newReq);
|
|
|
+ this.notification.error(`服务器异常,请联系管理员`, ``);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ if (ev instanceof HttpErrorResponse) {
|
|
|
+ console.warn('未可知错误,大部分是由于后端不支持CORS或无效配置引起', ev);
|
|
|
+ return throwError(ev);
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ return of(ev);
|
|
|
+ }
|
|
|
+
|
|
|
+ intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
|
|
+ // 统一加上服务端前缀
|
|
|
+ let url = req.url;
|
|
|
+ let newReq = req.clone({ url });
|
|
|
+ let igc = this.srv.get('incontrolGlobalConfig', { mode: 'none', type: 'm' });
|
|
|
+ if (!igc) {
|
|
|
+ const userCollection = this.srv.get('ic_userCollection', { mode: 'none', type: 'm' });
|
|
|
+ if (userCollection) {
|
|
|
+ const globalConfig = incontrolUtils.getConfigByCode(userCollection?.configList || [], 'global_config');
|
|
|
+ if (globalConfig) {
|
|
|
+ igc = globalConfig.configOption.config;
|
|
|
+ } else {
|
|
|
+ // console.error('未从缓存中获取到userCollection.globalConfig');
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // console.error('未从缓存中获取到userCollection');
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const token = (this.injector.get(DA_SERVICE_TOKEN) as ITokenService).get()?.token;
|
|
|
+ if (token && token.length > 1) {
|
|
|
+ const userCollection = this.srv.get('ic_userCollection', { mode: 'none', type: 'm' });
|
|
|
+ const globalConfig = incontrolUtils.getConfigByCode(userCollection?.configList || [], 'global_custom_code');
|
|
|
+ const interceptorCode = globalConfig?.configOption?.config?.interceptorCode;
|
|
|
+ if (globalConfig?.configOption?.config?.enabledInterceptorCode && incontrolUtils.icIsNotNull(interceptorCode)) {
|
|
|
+ const customInit = new Function('parameter', interceptorCode);
|
|
|
+ const config = { req: newReq };
|
|
|
+ const parameter = {
|
|
|
+ eventName: 'interceptorCode',
|
|
|
+ config,
|
|
|
+ comp: this
|
|
|
+ };
|
|
|
+ const retData: any = customInit(parameter);
|
|
|
+ if (retData?.req) {
|
|
|
+ newReq = retData.req;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (incontrolUtils.icIsNotNull(igc?.sqlEncodeCofig?.encodeType)) {
|
|
|
+ if (igc.sqlEncodeCofig.encodeDebugger) {
|
|
|
+ console.log('before encode', newReq);
|
|
|
+ }
|
|
|
+ let urlHash = window.location.hash.replace('#', '');
|
|
|
+ if ((urlHash || '').replace(/\//g, '').length < 3) {
|
|
|
+ const firstMenuUrl = window.sessionStorage.getItem('ic_userFirstMenu');
|
|
|
+ if (firstMenuUrl && incontrolUtils.icIsNotNull(firstMenuUrl)) {
|
|
|
+ urlHash = firstMenuUrl;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ newReq = incontrolUtils.encodeApiSql(newReq, {
|
|
|
+ urlHash,
|
|
|
+ type: igc.sqlEncodeCofig.encodeType!,
|
|
|
+ publicKey: igc?.sqlEncodeCofig?.encodePubkey || ''
|
|
|
+ });
|
|
|
+ if (token && token.length > 1) {
|
|
|
+ let incontrolToken = incontrolUtils.RSALong(igc?.sqlEncodeCofig?.encodePubkey || '', `${token}-_-_-${urlHash}`);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 特殊处理 sys/page/part/getByIds
|
|
|
+ * 2023-07-05 09:09:09
|
|
|
+ * 将body字符串化
|
|
|
+ * 拼入incontrol-token中加密
|
|
|
+ * 把body加密
|
|
|
+ * 后台拆出incontrol-token的最后一段与解密后的body进行对比
|
|
|
+ */
|
|
|
+ if (url.includes('sys/page/part/getByIds')) {
|
|
|
+ const copyBody = JSON.stringify(deepCopy(newReq.body));
|
|
|
+ incontrolToken = incontrolUtils.RSALong(igc?.sqlEncodeCofig?.encodePubkey || '', `${token}-_-_-${urlHash}-_-_-${copyBody}`);
|
|
|
+ const copyBodyRSA = incontrolUtils.RSALong(igc?.sqlEncodeCofig?.encodePubkey || '', copyBody);
|
|
|
+ newReq = newReq.clone({ body: copyBodyRSA });
|
|
|
+ }
|
|
|
+
|
|
|
+ const headers: HttpHeaders = newReq.headers.append('incontrol-token', incontrolToken);
|
|
|
+ newReq = newReq.clone({ headers });
|
|
|
+ }
|
|
|
+ if (igc.sqlEncodeCofig.encodeDebugger) {
|
|
|
+ console.log('after encode', newReq);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (token && token.length > 1) {
|
|
|
+ const headers: HttpHeaders = newReq.headers.append('incontrol-token', token);
|
|
|
+ newReq = newReq.clone({ headers });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (igc?.confuseApiName) {
|
|
|
+ let tempUrl = incontrolUtils.isConfuseApi(newReq.url);
|
|
|
+ if (tempUrl) {
|
|
|
+ newReq = newReq.clone({ url: tempUrl! });
|
|
|
+ const tempBody = deepCopy(newReq.body);
|
|
|
+ if (incontrolUtils.icIsNotNull(tempBody['querySql'])) {
|
|
|
+ tempBody['queryStr'] = tempBody['querySql'];
|
|
|
+ delete tempBody.querySql;
|
|
|
+ newReq = newReq.clone({ body: tempBody! });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return next.handle(newReq).pipe(
|
|
|
+ mergeMap((event: any) => {
|
|
|
+ // 允许统一对请求错误处理
|
|
|
+ if (event instanceof HttpResponseBase) {
|
|
|
+ return this.handleData(newReq, event);
|
|
|
+ }
|
|
|
+ // 若一切都正常,则后续操作
|
|
|
+ return of(event);
|
|
|
+ }),
|
|
|
+ catchError((err: HttpErrorResponse) => this.handleData(newReq, err))
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|