美文网首页ionic2实战ionic2
ionic2实战-自定义http拦截器

ionic2实战-自定义http拦截器

作者: 昵称已被使用_ | 来源:发表于2017-02-26 18:35 被阅读4519次

2017年6月11日注:

本文的实现方式是代理了angular http,现在有一个轻量级的实现,只需一个ts文件,完整代码看这里
两种方式各有有点,各位自己斟酌,建议用轻量级的实现方式

为什么要http拦截器

  • 想拦截所有http请求,在全局范围下处理异常
  • 想在请求前显示一个loading,请求完成关闭这个loading
  • 所有http统一传token

直接上代码

  • 我的app.module.ts完整代码,注意看代码后面的图片
import {NgModule, ErrorHandler} from '@angular/core';
import {IonicApp, IonicModule, IonicErrorHandler} from 'ionic-angular';
import {Storage} from '@ionic/storage';
import {MyApp} from './app.component';
import {LoginModule} from '../pages/login/login.module';
import {TabsPage} from '../pages/tabs/tabs';
import {ContactModule} from '../pages/contact/contact.module';
import {HomeModule} from '../pages/home/home.module';
import {MineModule} from '../pages/mine/mine.module';

import {NativeService} from "../providers/NativeService";
import {HttpIntercept} from "../providers/HttpIntercept";
import {HttpService} from "../providers/HttpService";
import {FileService} from "../providers/FileService";
import {Helper} from "../providers/Helper";
import {Utils} from "../providers/Utils";
import {TestModule} from "../pages/test/test.module";
import {Http, XHRBackend, RequestOptions} from "@angular/http";
import {HttpInterceptHandle} from "../providers/HttpInterceptHandle";


@NgModule({
  declarations: [MyApp, TabsPage],
  imports: [IonicModule.forRoot(MyApp, {
    backButtonText: '',
    iconMode: 'ios',
    modalEnter: 'modal-slide-in',
    modalLeave: 'modal-slide-out',
    pageTransition: 'ios'
  }), LoginModule, HomeModule, ContactModule, MineModule, TestModule],
  bootstrap: [IonicApp],
  entryComponents: [MyApp, TabsPage],
  providers: [HttpInterceptHandle, {provide: ErrorHandler, useClass: IonicErrorHandler},
    {
      provide: Http,
      useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, httpInterceptHandle: HttpInterceptHandle) => {
        return new HttpIntercept(backend, defaultOptions, httpInterceptHandle);
      },
      deps: [XHRBackend, RequestOptions, HttpInterceptHandle]
    },
    Storage, NativeService, HttpService, FileService, Helper, Utils]
})
export class AppModule {
}

app.module.ts完整代码
  • HttpInterceptHandle.ts完整代码
/**
 * Created by yanxiaojun617@163.com on 2-25.
 */
import {Events} from 'ionic-angular';
import {Injectable} from '@angular/core';
import {NativeService} from "./NativeService";

@Injectable()
export class HttpInterceptHandle {
  constructor(public events: Events, public nativeService: NativeService) {
    events.subscribe('request:before', (url, options) => {
      nativeService.showLoading();
      console.log('%c 请求前 %c', 'color:blue', '', 'url', url, 'options', options);
    });

    events.subscribe('request:success', (url, options, res) => {
      nativeService.hideLoading();
      console.log('%c 请求成功 %c', 'color:green', '', 'url', url, 'options', options, 'res', res);
    });

    events.subscribe('request:error', (url, options, error) => {
      nativeService.hideLoading();
      console.log('%c 请求失败 %c', 'color:red', '', 'url', url, 'options', options, 'error', error);
      let status = error.status;
      if (status === 0) {
        nativeService.showToast('请求响应错误,请检查网络');
      } else if (status === 404) {
        nativeService.showToast('请求链接不存在,请联系管理员');
      } else if (status === 500) {
        nativeService.showToast('服务器出错,请稍后再试');
      } else {
        nativeService.showToast('未知错误,请检查网络');
      }
    });
  }

}

`HttpInterceptHandle.ts`完整代码
  • HttpInterceptHandle.ts完整代码
/**
 * Created by yanxiaojun617@163.com on 12-27.
 */
import {Injectable} from '@angular/core';
import {Http, Response, RequestOptions, ConnectionBackend, RequestOptionsArgs} from '@angular/http';


import {Observable} from "rxjs";
import {HttpInterceptHandle} from "./HttpInterceptHandle";

@Injectable()
export class HttpIntercept extends Http {
  constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, public _: HttpInterceptHandle) {
    super(backend, defaultOptions);
  }

  get(url: string, options ?: RequestOptionsArgs): Observable < Response > {
    this._.events.publish("request:before", url, options);
    return Observable.create((observer) => {
      super.get(url, options).subscribe(res => {
        this._.events.publish("request:success", url, options, res);
        observer.next(res);
      }, err => {
        this._.events.publish("request:error", url, options, err);
        observer.error(err);
      });
    });
  }

  post(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
    this._.events.publish("request:before", url, options);
    return Observable.create((observer) => {
      super.post(url, body, options).subscribe(res => {
        this._.events.publish("request:success", url, options, res);
        observer.next(res);
      }, err => {
        this._.events.publish("request:error", url, options, err);
        observer.error(err);
      });
    });
  }

  put(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
    this._.events.publish("request:before", url, options);
    return Observable.create((observer) => {
      super.put(url, body, options).subscribe(res => {
        this._.events.publish("request:success", url, options, res);
        observer.next(res);
      }, err => {
        this._.events.publish("request:error", url, options, err);
        observer.error(err);
      });
    });
  }

  delete(url: string, options ?: RequestOptionsArgs): Observable < Response > {
    this._.events.publish("request:before", url, options);
    return Observable.create((observer) => {
      super.delete(url, options).subscribe(res => {
        this._.events.publish("request:success", url, options, res);
        observer.next(res);
      }, err => {
        this._.events.publish("request:error", url, options, err);
        observer.error(err);
      });
    });
  }

  patch(url: string, body: any, options?: RequestOptionsArgs): Observable<Response> {
    this._.events.publish("request:before", url, options);
    return Observable.create((observer) => {
      super.patch(url, body, options).subscribe(res => {
        this._.events.publish("request:success", url, options, res);
        observer.next(res);
      }, err => {
        this._.events.publish("request:error", url, options, err);
        observer.error(err);
      });
    });
  }


  head(url: string, options ?: RequestOptionsArgs): Observable < Response > {
    this._.events.publish("request:before", url, options);
    return Observable.create((observer) => {
      super.head(url, options).subscribe(res => {
        this._.events.publish("request:success", url, options, res);
        observer.next(res);
      }, err => {
        this._.events.publish("request:error", url, options, err);
        observer.error(err);
      });
    });
  }


  options(url: string, options ?: RequestOptionsArgs): Observable < Response > {
    this._.events.publish("request:before", url, options);
    return Observable.create((observer) => {
      super.options(url, options).subscribe(res => {
        this._.events.publish("request:success", url, options, res);
        observer.next(res);
      }, err => {
        this._.events.publish("request:error", url, options, err);
        observer.error(err);
      });
    });
  }

}

`HttpInterceptHandle.ts`代码

测试

ionic2实战-自定义http拦截器.gif

最后

有个小问题需要修改一下app.module.ts,详情看这里
参考
源代码已上传到github

由于ionic版本更新较快,有些写法可能改变来不及更新简书,请以github代码为准

相关文章

网友评论

  • 差不多先生_2517:老哥,现在fundebug收费了,还有什么好用的吗?
    昵称已被使用_:@差不多先生_2517 免费版,3000事件数/月,1个成员,1个项目,数据保存7天
    所以暂时可以给每个项目建一个账号用
    差不多先生_2517:@小军617 之前跟着你用感觉挺好用,但是要钱了,心塞啊
    昵称已被使用_:@差不多先生_2517 没有啊:joy:
  • 7727ed2edd50:大佬,,我有一个问题,,,我的ionic3的项目升级为angular5 了,HTTP被废弃了,,怎么用httpClient来替换呢
    昵称已被使用_:@差不多先生_2517 等ionic4发布release版本再搞
    差不多先生_2517:ng5的httpClient,老哥们搞了木有,想参考参考:yum:
    昵称已被使用_:@seven_Try 这个你要按这文档改造,我还没改造了https://angular.cn/guide/http
  • 差不多先生_2517:楼主,我又来了 = =还没解决 , , , 第一次请求 this.homeService.getTime().subscribe(res1 => { 第二次嵌套请求
    this.homeService.addDownLoad(apkDownLoadData).subscribe(res => {
    }
    }我请求这样写的,有的要先获取到时间,成功后在进行下一次请求,就这样出现了两次loading,要是嵌套多一层,那就要更多一次,,
    差不多先生_2517:@小军617 军哥解释的完美,很具体很详细,我会更多推荐给别人:yum:
    昵称已被使用_:@小军617 详情看NativeService.ts中showLoading()和hideLoading()
    https://github.com/yanxiaojun617/ionic2_tabs/blob/master/src/providers/NativeService.ts

    https://github.com/yanxiaojun617/ionic2_tabs/blob/master/src/providers/GlobalData.ts
    昵称已被使用_:1.由于showLoading()和hideLoading()写在公共的HttpService中,所以每次请求都会弹出loading.除非不写在HttpService中,而是手动控制show和hide.但这样太麻烦了.所以你想在所有请求前loading显示,所有请求结束loading关闭只能手动控制
    在说说我的实现:
    2.每次请求都会弹出loading,但有些请求不需要loading,所以在GlobalData中添加了showLoading boolean属性.调用showLoading()方法判断如果这个属性为false则不弹出.具体使用就是在请求前设置this.globalData.showLoading = false;注意为了方便我在hideLoading()方法中设置这个属性为true
    3.请求完成会调用hideLoading()方法,页面渲染也需要时间,所以loading.dismiss()做了200毫秒延迟..刚好嵌套循环第二个http发出的时候由于第一个http请求做了延迟,第二次loading不会弹出.
  • 差不多先生_2517:楼主大人,使用轻量版出现loading会弹多次。是在我同时请求多个接口的时候
    差不多先生_2517:@小军617 我说错了,不是同时,是嵌套请求
    昵称已被使用_:@差不多先生_2517 https://github.com/yanxiaojun617/ionic2_tabs/blob/master/src/providers/NativeService.ts 看showLoading方法
  • 差不多先生_2517:window.loacalstorage 和 ionic的 this.storage有什么优劣和不同嘛:stuck_out_tongue:
    昵称已被使用_:@差不多先生_2517 https://docs.fundebug.com/
    差不多先生_2517:@小军617 明白了,谢谢啦, 哦哦 还有个问题 你项目中用的 declare var fundebug; 需要下载什么插件之类的吗
    昵称已被使用_:@差不多先生_2517 ionic的storage可以根据配置使用不同的存储.可以配置sqlite,indexeddb,websql,localstorage.默认是localstorage
  • ec8486ad314c:军神,github上的代码,clone下来,怎么没有看到拦截器的代码啊?
    ec8486ad314c:@小军617 谢谢,军神
    :+1:
    昵称已被使用_:@love_cola 没必要使用拦截器了.所有http请求都经过,这个request方法,所以这个request方法就相当于拦截器了.
    https://github.com/yanxiaojun617/ionic2_tabs/blob/master/src/providers/HttpService.ts
  • Hoistthecolors:大神,拦截器 能不在https 协议下 jsonp请求把 头部信息 headers 带过去???
  • 6acc73bb8b9f:军神 这个拦截器可以拦截response.header里面的内容吗?
  • e7391ae9cfe3:你好,我刚学习ionic2的开发,我Corp了你的代码到我的demo里,但是有报错,能不能麻烦看一下是什么问题,十分感谢。
    Unhandled Promise rejection: Provider parse errors:
    Cannot instantiate cyclic dependency! HttpInterceptHandle ("[ERROR ->]"): in NgModule AppModule in ./AppModule@-1:-1 ; Zone: <root> ; Task: Promise.then ; Value: Error: Provider parse errors:
    Cannot instantiate cyclic dependency! HttpInterceptHandle ("[ERROR ->]"): in NgModule AppModule in ./AppModule@-1:-1
    at NgModuleProviderAnalyzer.parse (vendor.js:93512)
    at NgModuleCompiler.compile (vendor.js:99985)
    at JitCompiler._compileModule (vendor.js:108243)
    at createResult (vendor.js:108183)
    at t.invoke (polyfills.js:3)
    at r.run (polyfills.js:3)
    at polyfills.js:3
    at t.invokeTask (polyfills.js:3)
    at r.runTask (polyfills.js:3)
    at o (polyfills.js:3) Error: Provider parse errors:
    Cannot instantiate cyclic dependency! HttpInterceptHandle ("[ERROR ->]"): in NgModule AppModule in ./AppModule@-1:-1
    at NgModuleProviderAnalyzer.parse (http://localhost:8100/build/vendor.js:93512:19)
    at NgModuleCompiler.compile (http://localhost:8100/build/vendor.js:99985:24)
    at JitCompiler._compileModule (http://localhost:8100/build/vendor.js:108243:73)
    at createResult (http://localhost:8100/build/vendor.js:108183:26)
    at t.invoke (http://localhost:8100/build/polyfills.js:3:9283)
    at r.run (http://localhost:8100/build/polyfills.js:3:4452)
    at http://localhost:8100/build/polyfills.js:3:14076
    at t.invokeTask (http://localhost:8100/build/polyfills.js:3:9967)
    at r.runTask (http://localhost:8100/build/polyfills.js:3:5143)
    at o (http://localhost:8100/build/polyfills.js:3:2203)
  • debe5ceefa48:你好,请问Events在angular4哪个模块中import
    昵称已被使用_:import {Events} from "ionic-angular";
  • 23bb1d79aae5:你好,请问你是怎么用Android Studio 编写ionic2 的代码的?
    昵称已被使用_:我没用过Android Studio,用的是WebStorm
    054482dd7014:这个应该是WebStorm
  • 0bfe03bd21ed:使用后,请求偶尔会报EXCEPTION: Uncaught (in promise): false
    ErrorHandler.handleError @ error_handler.js:47
    IonicErrorHandler.handleError @ ionic-error-handler.js:56
    next @ application_ref.js:272
    schedulerFn @ async.js:82
    SafeSubscriber.__tryOrUnsub @ Subscriber.js:223
    SafeSubscriber.next @ Subscriber.js:172
    Subscriber._next @ Subscriber.js:125
    Subscriber.next @ Subscriber.js:89
    Subject.next @ Subject.js:55
    EventEmitter.emit @ async.js:74
    NgZone.triggerError @ ng_zone.js:278
    onHandleError @ ng_zone.js:257
    t.handleError @ polyfills.js:3
    e.runGuarded @ polyfills.js:3
    r @ polyfills.js:3
    i @ polyfills.js:3
    error_handler.js:52 ORIGINAL STACKTRACE:
    ErrorHandler.handleError @ error_handler.js:52
    IonicErrorHandler.handleError @ ionic-error-handler.js:56
    next @ application_ref.js:272
    schedulerFn @ async.js:82
    SafeSubscriber.__tryOrUnsub @ Subscriber.js:223
    SafeSubscriber.next @ Subscriber.js:172
    Subscriber._next @ Subscriber.js:125
    Subscriber.next @ Subscriber.js:89
    Subject.next @ Subject.js:55
    EventEmitter.emit @ async.js:74
    NgZone.triggerError @ ng_zone.js:278
    onHandleError @ ng_zone.js:257
    t.handleError @ polyfills.js:3
    e.runGuarded @ polyfills.js:3
    r @ polyfills.js:3
    i @ polyfills.js:3
    error_handler.js:53 Error: Uncaught (in promise): false
    305c00be0194:本人第一次做移动开发,楼主的文章给了很大的帮助,先谢谢了,有个问题想问一下。
    我在业务处理的Service中通过angular http调用webservice返回数据,如下:
    this.http.post(this.sendRepairProviderPath, body, options)
    .toPromise()
    .then(res => res.json())
    .catch(err => {
    this.handleError(err);
    });

    最初拦截器运行使用后后续的程序就不执行了,我在observer.next(res)后添加了一句
    observer.complete();才可以继续执行并返回所需的数据结果。

    后来测试时发现拦截器的http post超时就是 ERR_CONNECTION_TIMED_OUT 好像只能在浏览器测试环境中生效提示用户网络错误,build生成APK后安装到真机环境后好像一直在尝试连接,拦截器的404错误并没有发生。
    不知道说明白没有,初次做此类项目,请帮忙解答谢谢
    0bfe03bd21ed:@小军617 多谢回复,拦截器没问题,应该是我使用问题
    昵称已被使用_:没有吧,没人反应过

本文标题:ionic2实战-自定义http拦截器

本文链接:https://www.haomeiwen.com/subject/tuoowttx.html