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

搭建通用类

由于之前用的是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, next: HttpHandler): Observable> {
        if (this.id == "" && !req.url.includes('json')) {
            this.id = this.message.loading('请稍后...', { nzDuration: 0 }).messageId;
        }
        let secureReq: HttpRequest = 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) => {
                        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项目开发总结(二)_第1张图片
左边的地址栏里填写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文件中做改动。
这里写图片描述
在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((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,就可以联系之前的读取基地址相关联了。

你可能感兴趣的:(Summary)