Flutter GraphQL中如何拦截获取HttpCode

  今天继续补上早一阵聊Graphql_flutter的后遗症,上一篇文章《Flutter中使用GraphQL进行数据请求》

前言

  在《Flutter中使用GraphQL进行数据请求》文章中我们说了Graphql的诞生历史,它的目的、它的使用方法等。虽然说在查询接口多变一的状况下使用还是挺方便的,减少了接口的调用,本来一个页面需要好几个接口的,这下因Graphql,变为只需要使用一个接口搞定,真香是真香。但是,它还有点问题,因为Graphql它封装了Http StatusCode,也就是说它不给你返回Http状态码了,它给封装成它自己的Exception了。这就尴尬了,比如说token过期返回401,你捕获不了;比如说服务器异常250它哥500,你捕获不了;一曲凉凉~~

  那这里我通过Google,和自己研究了下Graphql_flutter里的源码,发现其实可以通过自定义Link来捕获Http状态码,其实通过查看HttpLink可以发现,它对于http的异常状态码,其实是做了一层封装的。

  而我们以下的拦截Http状态码都是基于pub.dev上graphql_flutter库来做处理。我这里主要是做了两个版本的研究拦截Http状态码:第一种是3.1.0一下版本;第二种是5.0.0支持空安全以上版本; 也就是说4.0.0-4.0.1断层了,没去管了哈~

一、适用于3.1.0一下版本拦截Http状态码

  因为我已经将拦截器作为插件上传到pub.dev上了,大家可以直接引用。

1.1 使用pub.dev上graphql_intercept_http_code_link

在graphql_flutter: 3.1.0以下版本使用,在pubspec.yaml中加入

dependencies:
  flutter:
    sdk: flutter
  graphql_flutter: 3.1.0
  graphql_intercept_http_code_link: 1.0.0
1.2 在《Flutter中使用GraphQL进行数据请求》文章中的GraphQLUtil类中引用
  Future<GraphQLClient> getGraphQLClient() async {
    Map<String, String> headers = {};
    HttpLink httpLink = HttpLink(uri: baseUrl, headers: headers);
    //定义GraphQLInterceptHttpCodeLink
    GraphQLInterceptHttpCodeLink errorLink =
    GraphQLInterceptHttpCodeLink(httpResponseHandler: (fetchResult, streamResponse) {
      //这里已经捕获了状态码>300的http status code,做一些处理
      //TODO deal with http status code
      dealWithHttpStatusCode(streamResponse);
    });
    GraphQLClient client =
    GraphQLClient(cache: InMemoryCache(), link: errorLink.concat(httpLink));

    return client;
  }

//异常Http请求,根据状态码做相应处理
void dealWithHttpStatusCode(StreamedResponse streamResponse) async {
    Response response;
    if (streamResponse != null) {
      switch (streamResponse.statusCode) {
        case HttpStatus.notModified:
        case HttpStatus.badRequest:
        ...
        case HttpStatus.badGateway:
        case HttpStatus.serviceUnavailable:
          break;
        default:
          response.statusCode = streamResponse.statusCode;
          break;
      }
      response.statusMessage = streamResponse.reasonPhrase;
    }
  }

1.3 自定义拦截代码一览

  大家可以看一看怎么做的拦截http状态码,实际上,因为HttpLink在进行http请求之后,返回了stream。它也会把异常http状态码进行封装,把response塞在一个字典Map的response字段里,所以我们可以取出来然后拦截返回。

HttpLink部分源码如下:
//这是HttpLink一个StreamController里重新的onListen方法
Future<void> onListen() async {
  StreamedResponse response;

  try {
    // httpOptionsAndBody.body as String
    final BaseRequest request = await _prepareRequest(parsedUri, operation, config);
    
    //进行网络请求
    response = await fetcher.send(request);
    
    //这里就是将reponse塞到一个Map的repsonse字段里,我们获取状态码就从这里入手
    operation.setContext(<String, StreamedResponse>{
      'response': response,
    });

    //这里实际上就是在包装返回Result,所以我们取不到状态码
    final FetchResult parsedResponse = await _parseResponse(response);

    controller.add(parsedResponse);
  } catch (failure) {
    ...
    controller.addError(translated);
  }

  await controller.close();
}
我们的拦截代码如下(大家有时间可以深入瞧一瞧很简单):
import 'dart:async';
import 'package:http/http.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:graphql/internal.dart';

typedef HttpResponseHandler = void Function(FetchResult, StreamedResponse);

class GraphQLInterceptHttpCodeLink extends Link {
  HttpResponseHandler httpResponseHandler;

  GraphQLInterceptHttpCodeLink({this.httpResponseHandler})
      : super(
          request: (Operation operation, [NextLink forward]) {
            StreamController<FetchResult> controller;

            Future<void> onListen() async {
              await controller
                  .addStream(forward(operation).map((FetchResult result) {
                if (result != null && result.errors != null) {
                  StreamedResponse response =
                      operation.getContext()["response"];
                  if ((response != null || response.statusCode >= 300) &&
                      httpResponseHandler != null) {
                    httpResponseHandler(result, response);
                  }
                }
                return result;
              }).handleError((error) {
                StreamedResponse response = operation.getContext()["response"];
                if ((response != null || response.statusCode >= 300) &&
                    httpResponseHandler != null) {
                  String message;
                  String errorMessage =
                      error.message != null ? error.message : '';
                  if (!isStringEmpty(errorMessage)) {
                    List msgList = errorMessage.split(':');
                    if (msgList.length >= 2) {
                      message = msgList[1].toString().trim();
                    }
                  }

                  StreamedResponse streamedResponse = StreamedResponse(
                      response.stream, response.statusCode,
                      reasonPhrase: isStringEmpty(message)
                          ? response.reasonPhrase
                          : message);

                  httpResponseHandler(null, streamedResponse);
                }
                throw error;
              }));

              await controller.close();
            }

            controller = StreamController<FetchResult>(onListen: onListen);

            return controller.stream;
          },
        );

  static bool isStringEmpty(String value) {
    return value == null || value.length <= 0;
  }
}

二、支持5.0.0以上空安全使用版本

  因为Flutter2.0 在pubspec.yaml文件中sdk: ">=2.12.0 <3.0.0"。sdk必须支持空安全后,我们也需要升级graphql_flutter以支持空安全,而我在实际操作过程中,发现graphql_flutter升级后,原来的拦截状态码不适用了。这也是Flutter比价恶心的地方,很多库升级不向下兼容。直接面目全非的改。所以拦截器也得跟着改了。

2.1 使用pub.dev上graphql_intercept_http_code_link

在graphql_flutter: 5.0.0以上版本使用,在pubspec.yaml中加入

dependencies:
  flutter:
    sdk: flutter
  graphql_flutter: 5.0.0
  graphql_intercept_http_code_link: 2.0.0
2.2 在《Flutter中使用GraphQL进行数据请求》文章中的GraphQLUtil类中引用

  这里的使用和1.2中的差不多,这里就不再赘述了。

2.3 自定义拦截代码一览

  在graphql_flutter 5.0.0中使用到的HttpLink类中,做了一些修改,它不再将repsonse直接放入Map中了,它将状态码大于300的请求直接返回抛出异常,这时,嘿嘿,我们只需要拦截捕获异常就可以了。

HttpLink部分源码如下:
@override
Stream<Response> request(
  Request request, [
  NextLink? forward,
]) async* {
  //进行网络请求
  final httpResponse = await _executeRequest(request);

  //封装response
  final response = await _parseHttpResponse(httpResponse);

  //状态码大于300的抛出异常
  if (httpResponse.statusCode >= 300 ||
      (response.data == null && response.errors == null)) {
    throw HttpLinkServerException(
      response: httpResponse,
      parsedResponse: response,
    );
  }

  yield Response(
    data: response.data,
    errors: response.errors,
    context: _updateResponseContext(response, httpResponse),
  );
}
我们拦截的代码如下:
import 'dart:async';
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:http/http.dart' as Http;

typedef HttpResponseHandler = void Function(Response?, Http.Response);

class GraphQLInterceptHttpCodeLink extends Link {
  HttpResponseHandler httpResponseHandler;
  late StreamController<Response> controller;
  late Request gqlRequest;
  NextLink? forward;

  GraphQLInterceptHttpCodeLink({required this.httpResponseHandler});

  @override
  Stream<Response> request(Request request, [NextLink? forward]) {
    gqlRequest = request;
    this.forward = forward;
    controller = StreamController<Response>(onListen: onListen);

    return controller.stream;
  }

  void onListen() async {
    await controller.addStream(forward!(gqlRequest).handleError((error) {
      if(error != null && error is HttpLinkServerException){
        Http.Response httpResponse = error.response;
        if (httpResponse != null) {
            httpResponseHandler(null, httpResponse);
        }
      }
      throw error;
    }
    ));

    await controller.close();
  }
}

遇到问题,多多查看源码,百google而不得琪姐的时候,源码能带给你不一样的惊喜。

三、 结语

  没啥好结的~ 想说源码很重要,苏联时期摸着毛熊过河,21年摸着鹰酱过河,我们学一学摸着源码过活。O(∩_∩)O哈哈~

申明:禁用于商业用途,如若转载,请附带原文链接。https://www.jianshu.com/p/be5f83ebc0b8蟹蟹~

PS: 写文不易,觉得没有浪费你时间,请给个关注和点赞~ 😁

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容