本项目从属于笔者的Web开发入门与最佳实践前端开源项目系列。如果对于HTTP协议与规范尚不掌握的建议阅读HTTP 基础与变迁一文。如果对于REST尚不掌握的推荐阅读来自微软的接口设计指南以及来自于PayPal的RESTful API标准

Fluent Fetcher:兼容Weapp的流式网络请求库

笔者一直比较喜欢使用标准的Fetch API来进行浏览器端的网络请求,同时使用的是isomorphic-fetch保证接口类的可测试性。而本项目则是笔者借鉴了SuperAgent API的风格基于Fetch进行的二次封装,主要是提供了流式风格的构造与使用方法,同时对常见功能进行了扩展。另外,fluent-fetcher还原生支持Weapp,基于fluent-fetcher构造的接口类可以直接在Weapp环境下使用。

Basic Usage

Installation

可以使用npm install fluent-fetcher --save安装本项目,或者使用yarn add fluent-fetcher安装使用。

Instantiate:实例化

直接在需要使用的地方以import导入即可:

//@flow
import FluentFetcher from 'fluent-fetcher';

我们可以直接以实例化方式创建请求实例:

const host = "jsonplaceholder.typicode.com";
let fluentFetcher = new FluentFetcher({scheme: "http", host});

其中可选的构造参数有:

constructor(
    {
        scheme = "http", //请求协议
        host = "api.com",  //域名
        encoding = "utf8",  //编码
        acceptType = "json" //接收的返回数据类型
    } = {}
)
{...}

而如果在项目中我们存在多个数据源,建议是在接口公共父类中创建多个数据源实例,这样具体的接口实现类皆可以使用使用this.xxxClient方式进行请求。

// @flow
//E路数据的服务端地址
const edataHost = 'api.edata.com';

//公共资源的服务端地址
const ggzyHost = 'api.ggzy.com';

export default class API {

  //用于抓取公共资源数据的API
  ggzyAPIClient: FluentFetcher = null;

  //EDATA,用于抓取E路数据的API
  edataAPIClient: FluentFetcher = null;

  /**
   * @function 默认构造函数
   */
  constructor() {

    //构造请求体
    this.ggzyAPIClient = new FluentFetcher({
      scheme: 'http',
      host: ggzyHost,
      responseContentType: "json"
    }).header({
      key: 'UserAgent',
      value: 'wap'
    }).header({
      key: 'Content-Type',
      value: 'application/json'
    });

    this.edataAPIClient = new FluentFetcher({
      scheme: 'http',
      host: edataHost,
      responseContentType: "json"
    }).header({
      key: 'UserAgent',
      value: 'wap'
    }).header({
      key: 'Content-Type',
      value: 'application/json'
    });

  }

}

注意,如果你是在Weapp环境下,那么FluentFetcher会自动使用wx.request作为请求载体。

GET

//测试GET类型请求
describe('GET请求测试', function () {

  //https://jsonplaceholder.typicode.com/posts?userId=1
  it('获取博文', function (done) {
    fluentFetcher
      .parameter({"userId": "1"})
      .get("/posts")
      .build()
      .then((data) => {
        expect(data).to.be.a('array');
        expect(data.length).to.be.above(0);
        done();
      }).catch((error) => {
      done(error);
    });
  });


});

POST


//测试POST类型请求
describe('POST请求测试', function () {

  //https://jsonplaceholder.typicode.com/posts?userId=1
  it('创建博文@表单编码格式', function (done) {
    fluentFetcher
      .parameter({
        title: 'foo',
        body: 'bar',
        userId: 1
      })
      .post("/posts", 'x-www-form-urlencoded')
      .build()
      .then((data) => {
        expect(data).to.be.a('object');
        expect(data.id).to.equal(101);
        expect(data.title).to.equal('foo');
        expect(data.body).to.equal('bar');
        expect(data.userId).to.equal(1);

        done();
      }).catch((error) => {
      done(error);
    });
  });

  //https://jsonplaceholder.typicode.com/posts?userId=1
  it('创建博文@JSON格式', function (done) {
    fluentFetcher
      .parameter({
        title: 'foo',
        body: 'bar',
        userId: 1
      })
      .post("/posts")
      .build()
      .then((data) => {
        expect(data).to.be.a('object');
        expect(data.id).to.equal(101);
        expect(data.title).to.equal('foo');
        expect(data.body).to.equal('bar');
        expect(data.userId).to.equal(1);

        done();
      }).catch((error) => {
      done(error);
    });
  });


});

PUT/DELETE

类似于POST请求方法。

Advanced Usage

Encode:编码

项目使用了isomorphic-urlencode作为通用的编解码工具,正常的会支持UTF8与GBK两种编码方案。当我们以x-www-form-urlencoded格式发起POST/PUT/DELETE请求时,会自动会请求体进行编码工作。另外,如果我们使用pathSegment添加请求路径时候,也会自动进行编码操作。

Mock

在实际的项目中,譬如微信小程序里,可能我们的服务端尚未准备好或者域名没有审核通过,我们需要在前端进行Mock操作以模拟正常业务流程的返回。笔者在这里选定的Mock方案个人感觉还是比较轻量级的,即直接在构造实例时将你需要的Mock地址对应的数据传入即可。

//测试MOCK
describe('MOCK请求测试', function () {

  it('对于MOCK数据应该正常返回本地值', function (done) {


    fluentFetcher.get('/mock').mock({'/mock': {test: 'data'}}).build().then((data) => {

      expect(data).to.be.a('object');

      expect(data.test).to.equal('data');

      done();

    });

  });

});

CORS:跨域请求

直接使用cors()函数添加CORS支持即可。

Error:异常处理

异常处理是我们项目中常见的操作,在Fluent Fetch中对于所有非200或者300系列的请求皆会抛出异常:

    if (response.status >= 200 && response.status < 300) {
      return response
    } else {
      var error = new Error(response.statusText);
      error.response = response;
      throw error
    }

笔者是推荐使用async/await加上try-catch语法进行异常处理:

    try {
      //发起数据请求
      let result = await this.post(`/quotation/bom/${bom.id}`, data);

      return successResponse;

    } catch (err) {

      return internalServerErrorResponse;

    }

RoadMap

  • Cache:缓存

  • Retry:失败重试

  • Timeout:超时


王下邀月熊_Chevalier
22.5k 声望8.5k 粉丝

爱代码 爱生活 希望成为全栈整合师