欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Angular6 + Ng-Zorro项目开发总结(二)

程序员文章站 2022-07-14 09:31:13
...

搭建通用类

由于之前用的是Abp框架,框架封装的十分成熟,所以很多时候用起来都是知其然而不知其所以然。比如刚开始还没有意识到每次请求都传了token,因为在http方法的调用中,并没有看到它添加header,知道后面看network才知道每次都传了token,搜了全部的文件,关于token的,只有封装好的settoken方法和gettoken方法,到底是怎么加入header的十分费解,后来,想了想估计是拦截器的作用,找了找abp源码,果然有httpInceptor.js,代码一读,问题自然迎刃而解。

HttpInterceptor(HTTP拦截器)

关于这个东西的概念,官网是有十分清楚的解释的,通俗的来说就是你在每次发起http请求时,最后的都要经过拦截器来加工一次,然后返回的http response也是要先经过拦截器处理加工。
自定义拦截器需要创建一个服务,服务中必须有的方法是intercept方法。

@Injectable()
export class InterceptorService implements HttpInterceptor {
    constructor(
        private message: NzMessageService,
        private sessionService: SessionService) {

    }

    id: string = "";

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (this.id == "" && !req.url.includes('json')) {
            this.id = this.message.loading('请稍后...', { nzDuration: 0 }).messageId;
        }
        let secureReq: HttpRequest<any> = req;
        // const url = `${config.apiUrl}/api/`;   //添加api统一前缀
        let modifiedHeaders = new HttpHeaders();
        let token = document.cookie;
        if (token) {
            modifiedHeaders = req.headers.set('Authorization', `Bearer ${token}`);
        }
        secureReq = req.clone({
            url: req.url,
            headers: modifiedHeaders
        });
        const started = Date.now();
        let ok: string;
        if (req.url.includes('json')) {
            return next.handle(secureReq);
        }
        else {
            return next.handle(secureReq)
                .pipe(
                    catchError((res: HttpResponse<any>) => {
                        console.log(res);
                        let msg = "";
                        switch (res.status) {
                            case 401:
                                msg = "身份验证过期,请重新进入页面";
                                break;
                            case 200:
                                msg = "身份验证过期,请重新进入页面";
                                break;
                            case 404:
                                msg = "找不到地址";
                                break;
                            case 403:
                                msg = "业务错误";
                                break;
                            case 500:
                                msg = "服务器发生错误,请重试";
                                break;
                        }
                        this.showError(msg);
                        return Observable.create(res);
                    }),
                    finalize(() => {
                        const elapsed = Date.now() - started;  //可计算出请求所消耗时间
                        const msg = `${req.method} "${req.urlWithParams}" ${ok} in ${elapsed} ms.`;
                        console.log(msg);
                    }),
                    mergeMap(
                        // Succeeds when there is a response; ignore other events
                        (event: any) => {
                            if (event.status == 200) {
                                this.id = "";
                                this.message.remove();
                            }
                            return Observable.create(observer => observer.next(event));
                        }),

            );
        }


         showError(message: string) {
             this.message.remove();
             this.id = "";
            this.message.error(message, { nzDuration: 2000 })
         }
    }

这是我在项目中实际用的interceptor,我做的比较多的修改是在请求进来的时候,加载了ng-zorro的message的loading消息,这是个全局的提示消息,然后从cookie中读取的之前存入的token,将其放入header中,执行next.handle()方法,执行http请求,通过catcherror来捕获异常,根据异常的代码不同,来提示不同的错误。对了,这个地方用的pipe,catchError,finalize都是是rxjs6的新语法。
写完自己的拦截器,接下来就需要去项目中入口的module.ts文件中配置了,

    providers: [
        ...
        { provide: HTTP_INTERCEPTORS, useClass: InterceptorService, multi: true },
    ],

配置完成后,重新生成就生效了。

Http服务

这个地方就是根据不同的项目有不同的实现方法了,我这个项目中,后台用的技术是.Net Core,内部集成了swagger,这也为我提供了便利,我直接用NSwagStudio,来生成我的ts代码。贴个软件的图
Angular6 + Ng-Zorro项目开发总结(二)
左边的地址栏里填写swagger的json地址(一般来说只用修改前面的地址和端口就行了),填写完了过后,create local copy,swagger的json文件就生成出来了,然后右侧的outputs中,可以选择三种语言,这里我只是需要Typescript,所以后面两项我就没有勾选了,然后再下面的TypeScript的tab栏里面有一些配置,比如typescript的版本,angular和rxjs的版本,根据我的项目,我选择了相对应的版本,然后generate output就可以了。
这个地方比较难以理解的就是api的基地址,在生成的代码中是以这种形式获取的。

export const API_BASE_URL = new InjectionToken<string>('API_BASE_URL');

参考了一下之前用的abp框架,还是摸索出来了这个到底该怎么用。
首先需要在根模块进行配置

providers: [
  ...
   { provide: API_BASE_URL, useFactory: getRemoteServiceBaseUrl },
],

这里需要先从生成的文件中import API_BASE_URL。

import { API_BASE_URL } from './common-service';

然后这个地方用到的useFactory,用的getRemoteServiceBaseUrl方法,是需要我们自己实现的,我这里就在module文件中实现了。

export function getRemoteServiceBaseUrl(): string {
    return AppConsts.baseURL;
  }

返回的是我之前在初始化的时候配置在我的一个常量文件中的url。这样操作完成后就大功告成了。

项目初始化

上面用的url,如果是写死在ts文件中是可以行得通的,但为了保证有些时候接口地址可能会变,如果是要改部署到环境上的项目,那就需要再生成,再部署一次才行,相当的麻烦,所以可以尝试着把地址配在一个json文件中,每次进来的时候读取一次就行了,哪怕发生变化,也只需要在json文件中做改动。
Angular6 + Ng-Zorro项目开发总结(二)
在assets文件夹下新建appconfig.json,文件中写好配置。
由于需要在项目启动时读取这个文件,所以需要在初始化的时候进行读取,这里需要在根模块进行一些改动

{
   provide: APP_INITIALIZER,
   useFactory: appInitializerFactory,
   deps: [Injector, PlatformLocation],
   multi: true
},

这里的APP_INITIALIZER和Injector是angular本身就自带的,需要从angular/core中import。

import {  APP_INITIALIZER, Injector } from '@angular/core';

实现的这个这个方法,appInitializerFactory则需要自己定义,同样,我也在根模块中定义了。

export function appInitializerFactory(
    injector: Injector,
    platformLocation: PlatformLocation
  ) {
    return () => {
      return new Promise<boolean>((resolve, reject) => {
        let httpclient: HttpClient = injector.get(HttpClient);
        httpclient.get('assets/appconfig.json').toPromise().then((res: any) => {
          AppConsts.baseURL = res.remoteServiceBaseUrl;
          resolve(true);
        },
          (err) => {
            reject(err);
          })
      });
    }
  }

这里尝试了很多种返回方法,最后还是只有返回Promise才生效。赋值给AppConsts.baseURL,就可以联系之前的读取基地址相关联了。

相关标签: angular