概述

Remote Communication Kit中的@hms.collaboration.rcp(后续简称RCP)指的是远程通信平台(remote communication platform),RCP提供了网络数据请求功能,相较于Network Kit中HTTP请求能力,RCP更具易用性,且拥有更多的功能。在开发过程中,如果有些场景使用Network Kit中HTTP请求能力达不到预期或无法实现,那么就可以尝试使用RCP中的数据请求功能来实现。

接下来,本文将先介绍RCP与HTTP的区别,然后从使用RCP实现基础的网络请求、多表单提交、双向证书校验、DNS的相关设置、请求与响应拦截和捕获有关HTTP请求/响应流的详细信息等几个场景来介绍RCP拥有的能力。

RCP与HTTP的区别

为了方便了解RCP与HTTP的区别,可以从功能分类、功能名称和功能描述这三个方面进行对比,主要区别如下:

功能分类 功能名称 功能描述 HTTP RCP
基础功能 发送PATCH类型请求 以PATCH的方式请求 不支持 支持
基础功能 设置会话中URL的基地址 会话中URL的基地址将自动加在URL前面,除非URL是一个绝对的URL 不支持 支持
基础功能 取消自动重定向 HTTP请求不会自动重定向 不支持 支持
基础功能 拦截请求和响应 在请求后或响应前进行拦截 不支持 支持
基础功能 取消请求 发送请求前取消、发送请求过程中取消、请求接收后取消 不支持 支持
基础功能 响应缓存 是否使用缓存,请求时优先读取缓存。缓存跟随当前进程生效,新缓存会替换旧缓存 不支持 支持
基础功能 设置响应数据的类型 设置数据以何种方式返回,将要响应的数据类型可设置为string、object、arraybuffer等类型 支持 不支持
基础功能 定义允许的HTTP响应内容的最大字节数 服务器成功响应时,在获取数据前校验响应内容的最大字节数 支持 不支持
证书验证 自定义证书校验 自定义逻辑校验客户端和服务端的证书,判断是否可以连接 不支持 支持
证书验证 忽略SSL校验 在建立SSL连接时不验证服务器端的SSL证书 不支持 支持
DNS 自定义DNS解析 包括自定义DNS服务器或静态DNS规则 不支持 支持
rcp特有 捕获详细的跟踪信息 在会话中的HTTP请求期间捕获详细的跟踪信息。跟踪有助于调试、性能分析和深入了解通信过程中的数据流 不支持 支持
rcp特有 数据打点,获取HTTP请求的具体数据 HTTP请求各阶段的定时信息 不支持 支持

实现基础的网络请求

发送请求

通过RCP模块能够发起基础的网络请求,如GET、POST、HEAD、PUT、DELETE、PATCH、OPTIONS等请求。以PATCH请求为例,开发过程中经常会遇到发送请求修改资源的场景,假设有一个UserInfo,里面有userId、userName、 userGender等10个字段。可编辑功能因为需求,在某个特别的页面里只能修改userName,这时就可以用PATCH请求,来更新局部资源。

实现思路

在创建[session]会话后,通过创建请求对象并传入第二个参数且指定为PATCH,然后通过session.fetch()发起请求即可。

  1. 导入rcp模块。
  2. 创建headers,设置可接受的数据内容的类型为json字符串;创建modifiedContent,传入想要修改的内容。
  3. 调用rcp.createSession()创建通信会话对象session。
  4. 使用new rcp.Request()方法创建请求对象req。
  5. 调用session.fetch()方法发起请求。
  6. 获取响应结果。

核心代码

// 定义请求头
let headers: rcp.RequestHeaders = {
  'accept': 'application/json'
};
// 定义要修改的内容
let modifiedContent: UserInfo = {
  'userName': 'xxxxxx'
};
const securityConfig: rcp.SecurityConfiguration = {
  tlsOptions: {
    tlsVersion: 'TlsV1.3'
  }
};
// 创建通信会话对象
const session = rcp.createSession({ requestConfiguration: { security: securityConfig } });
// 定义请求对象rep
let req = new rcp.Request('http://example.com/fetch', 'PATCH', headers, modifiedContent);
// 发起请求
session.fetch(req).then((response) => {
  Logger.info(`Request succeeded, message is ${JSON.stringify(response)}`);
}).catch((err: BusinessError) => {
  Logger.error(`err: err code is ${err.code}, err message is ${JSON.stringify(err)}`);
});

设置会话中URL的基地址

会话中URL的基地址是在发起请求时用作请求地址的前缀。 当我们向服务器发起请求时,该请求的最终的请求地址由会话中URL的基地址与请求路径来构建的。 这使得我们可以将服务器的主机地址与公共路径隔离开来,方便管理和维护。

实现思路

会话中URL的基地址可通过RCP模块中的[SessionConfiguration]来进行设置,在sessionConfig对象中设置“baseAddress:‘http://api.example.com’ ”即可。

  1. 导入rcp模块。
  2. 设置sessionConfig对象中的baseAddress为http://api.example.com。
  3. 调用rcp.createSession()传入sessionConfig,创建通信会话对象session。

核心代码

// 定义sessionConfig对象
const sessionConfig: rcp.SessionConfiguration = {
  baseAddress: 'http://api.example.com',
  headers: {
    'authorization': 'Bearer YOUR_ACCESS_TOKEN',
    'content-type': 'application/json'
  },
  requestConfiguration:{
    security:{
      tlsOptions: {
        tlsVersion: 'TlsV1.3'
      }
    }
  }
};
// 创建通信会话对象,并传入sessionConfig
const session = rcp.createSession(sessionConfig);

实现多表单提交

开发过程中时常会遇到多表单提交的场景,例如在同页面下tab栏可切换多个Form表单组件,但只有一个提交按钮,各组件下的表单数据需要被一起提交。此时可以使用RCP模块中的[MultipartForm]来实现多表单提交的场景。

实现思路

在创建session会话后,通过new rcp.Request()的第四个参数传入MultipartForm,然后通过rcp.fetch()发起POST请求将多个表单数据携带上传至服务端。

  1. 导入rcp模块。
  2. 设置请求头类型、配置HTTP请求的超时值、HTTP请求中包含的cookie和设置传输数据范围。
  3. 调用rcp.createSession()创建通信会话对象。
  4. 通过new rcp.MultipartForm()设置多表单数据。
  5. 使用new rcp.Request()创建请求对象,调用session.fetch()方法发起请求。
  6. 处理响应结果。

核心代码

// 定义请求头类型
let headers: rcp.RequestHeaders = {
  'accept': 'application/json'
};
// 配置HTTP请求的超时值
let configuration: rcp.Configuration = {
  transfer: {
    timeout: {
      connectMs: 60000,
      transferMs: 60000
    }
  }
};
// HTTP请求中的Cookie
let cookies: rcp.RequestCookies = {
  'name1': 'value1',
  'name2': 'value2',
};
// 设置数据传输范围
let transferRange: rcp.TransferRange = {
  from: 100,
  to: 200
};

// 设置multipartFrom数据
const multiForm = new rcp.MultipartForm({
  'Form1': this.name, // string
  'Form2': this.hobbies, // string
  'Form3': {
    contentType: 'text/plain',
    remoteFileName: 'RemoteFileName',
    contentOrPath: '/file/to/Path'
  } // object
});

const securityConfig: rcp.SecurityConfiguration = {
  tlsOptions: {
    tlsVersion: 'TlsV1.3'
  }
};
// 创建通信会话对象
const session = rcp.createSession({ requestConfiguration: { security: securityConfig } });
// 定义请求对象req
let req =
  new rcp.Request('https://www.example.com', 'POST', headers, multiForm, cookies, transferRange, configuration);
req.content = multiForm;

// 发起请求
session.fetch(req).then((response) => {
  Logger.info(`Request succeeded, message is ${JSON.stringify(response)}`);
}).catch((err: BusinessError) => {
  Logger.error(`err: err code is ${err.code}, err message is ${JSON.stringify(err)}`);
});

实现双向证书校验

为验证服务端和客户端之间的身份和数据完整性,确保通信的安全性,可使用RCP接口能力实现双向证书校验能力。

实现对DNS的定制设置

通过RCP模块,能够实现对DNS的定制设置。开发人员可以按自己的需要配置DNS,包括自定义DNS服务器、自定义静态DNS规则和配置HTTPS上的DNS,都可以通过[DnsConfiguration]设置。DnsConfiguration中可设置dnsRules、dnsOverHttps。

  • dnsRules(配置DNS规则)

    自定义DNS服务器([DnsServers]):可指定自定义的DNS服务器提供解析服务。

    自定义静态DNS([StaticDnsRules]):有些时候,默认的DNS不能正常解析部分域名,就需要手动添加静态DNS。添加静态DNS后,如果hostname匹配,则优先使用指定的地址。

    自定义动态DNS([DynamicDnsRules]):除了添加静态DNS外,还可以添加动态DNS。动态DNS可看作一个可以根据hostname和port直接返回IP地址的函数,如果设置,则优先使用函数中返回的地址。

  • dnsOverHttps

    DNS over HTTPS配置([DnsOverHttpsConfiguration]):配置HTTPS上的DNS(DOH)设置,以加密的HTTPS协议进行DNS解析请求,避免原始DNS协议中用户的DNS解析请求被窃听或者修改的问题,来达到保护用户隐私的目的。如果设置,则优先使用DNS服务器解析的地址。

自定义DNS服务器

实现思路

先配置自定义的DNS服务器customDnsServers,在创建session会话时,通过requestConfiguration传入dns对象,指定dns对象中的dnsRules为customDnsServers。

  1. 导入rcp模块。
  2. 配置自定义的DNS服务器。
  3. 调用rcp.createSession()创建通信会话对象时,传入自定义的DNS服务器。

核心代码

// 配置自定义DNS服务器
const customDnsServers: rcp.DnsServers = [
  { ip: '8.8.8.8' },
  { ip: '8.8.4.4', port: 53 }
];
// 创建通信会话对象
const sessionWithCustomDns = rcp.createSession({
  requestConfiguration: {
    dns: {
      dnsRules: customDnsServers
    },
    security: {
      tlsOptions: {
        tlsVersion: 'TlsV1.3'
      }
    }
  }
});
// 发起请求
sessionWithCustomDns.get('http://www.example.com').then((response) => {
  Logger.info(`Request succeeded, message is ${JSON.stringify(response)}`);
}).catch((err: BusinessError) => {
  Logger.error(`err: err code is ${err.code}, err message is ${JSON.stringify(err)}`);
});

自定义静态DNS

实现思路

先配置静态DNS规则staticDnsRules,在创建session会话时,通过requestConfiguration传入dns对象,指定dns对象中的dnsRules为staticDnsRules 。

  1. 导入rcp模块。
  2. 配置静态DNS规则。
  3. 调用rcp.createSession()创建通信会话对象时,传入静态DNS规则。
// 当匹配到hostname时,优先使用指定的地址
const staticDnsRules: rcp.StaticDnsRules = [
  {
    host: 'example.com',
    port: 80,
    ipAddresses: ['192.168.1.1', '192.168.1.2']
  }
];
// 创建通信会话对象
const sessionWithCustomDns = rcp.createSession({
  requestConfiguration: {
    dns: {
      dnsRules: staticDnsRules
    },
    security: {
      tlsOptions: {
        tlsVersion: 'TlsV1.3'
      }
    }
  }
});
// 发起请求
sessionWithCustomDns.get('http://www.example.com').then((response) => {
  Logger.info(`Request succeeded, message is ${JSON.stringify(response)}`);
}).catch((err: BusinessError) => {
  Logger.error(`err: err code is ${err.code}, err message is ${JSON.stringify(err)}`);
});

配置HTTPS上的DNS

实现思路

先创建HTTPS上的DNS对象dohConfig,在创建session会话时,通过requestConfiguration传入dns对象,指定dns对象中的dnsOverHttps为dohConfig 。

  1. 导入rcp模块。
  2. 创建HTTPS上的DNS对象dohConfig。
  3. 调用rcp.createSession()创建通信会话对象时,传入dohConfig。
// DNS over HTTPS配置
const dohConfig: rcp.DnsOverHttpsConfiguration = {
  url: 'https://dns.example.com/dns-query',
  skipCertificatesValidation: true
};
// 创建通信会话对象
const sessionWithCustomDns = rcp.createSession({
  requestConfiguration: {
    dns: {
      dnsOverHttps: dohConfig
    },
    security: {
      tlsOptions: {
        tlsVersion: 'TlsV1.3'
      }
    }
  }
});
// 发起请求
sessionWithCustomDns.get('http://www.example.com').then((response) => {
  Logger.info(`Request succeeded, message is ${JSON.stringify(response)}`);
}).catch((err: BusinessError) => {
  Logger.error(`err: err code is ${err.code}, err message is ${JSON.stringify(err)}`);
});

实现请求与响应拦截

使用拦截器可以方便的对HTTP的请求与响应进行修改,您可以创建拦截器链,按需定制一组拦截器对您的网络请求/响应进行修改。RCP模块提供了拦截器能力,在[SessionConfiguration]中添加[Interceptors]参数,传入自定义的拦截器,即可在HTTP请求和响应的过程中添加拦截器功能。

拦截器工作原理

客户端发送HTTP请求,到达目标服务器之前,可以使用拦截器对HTTP的请求进行修改。如下图,定义了链式拦截器,RequestUrlChangeInterceptor拦截器(下文以拦截器1代替)和ResponseHeaderRemoveInterceptor拦截器(下文以拦截器2代替)。请求先被拦截器1拦截,该拦截器可以实现当网络质量差时,通过修改HTTP请求中的URL,来调整请求资源的大小。然后经过拦截器2,最后到达Internet。当请求到达目标服务器,服务器返回请求响应的结果给客户端之前,可以使用拦截器对HTTP的响应进行修改。如下图,响应先被拦截器2拦截,在响应返回给应用前检查和修改服务器的请求头。然后经过拦截器1,最后客户端接收响应结果。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

说明

RequestUrlChangeInterceptor拦截器和ResponseHeaderRemoveInterceptor拦截器都是自定义拦截器,需要开发者通过代码去实现内部逻辑。

拦截器的定义和使用

下面将介绍如何自定义拦截器,定义RequestUrlChangeInterceptor拦截器和ResponseHeaderRemoveInterceptor拦截器实现[rcp.Interceptor],可在[intercept()]方法中根据业务需求自定义处理逻辑,实现对请求/响应的修改。

  1. 导入rcp模块。
  2. 定义RequestUrlChangeInterceptor拦截器和ResponseHeaderRemoveInterceptor拦截器。
  3. 在intercept()方法中实现对请求/响应的修改逻辑。

核心代码

import { rcp } from '@kit.RemoteCommunicationKit';
import { url } from '@kit.ArkTS';
import Logger from '../common/Logger';
import { NetworkQualityProvider } from './NetworkStateSimulator';

// 定义RequestUrlChangeInterceptor拦截器
export class RequestUrlChangeInterceptor implements rcp.Interceptor {
  private readonly networkQualityProvider: NetworkQualityProvider;

  constructor(networkQualityProvider: NetworkQualityProvider) {
    this.networkQualityProvider = networkQualityProvider;
  }

  // 自定义请求处理逻辑
  async intercept(context: rcp.RequestContext, next: rcp.RequestHandler): Promise<rcp.Response> {
    if (context.request.method === 'GET' && !this.networkQualityProvider.isNetworkFast()) {
      Logger.info('[RequestUrlChangeInterceptor]: Slow network is detected');
      const parts = context.request.url.pathname.split('.');
      if (parts.length === 2) {
        const changed = url.URL.parseURL(context.request.url.href);
        changed.pathname = parts[0] + '_small.' + parts[1];
        Logger.info(`[RequestUrlChangeInterceptor]: Replace URL from "${context.request.url.href}" to "${changed}"`);
        AppStorage.setOrCreate('ReplacedInfo',`[RequestUrlChangeInterceptor]: Replace URL from "${context.request.url.href}" to "${changed}"`);
        context.request.url = changed;
      }
    } else {
      Logger.info('[RequestUrlChangeInterceptor]: Network is fast');
    }
    return next.handle(context);
  }
}

// 定义ResponseHeaderRemoveInterceptor拦截器
export class ResponseHeaderRemoveInterceptor implements rcp.Interceptor {
  // 自定义响应处理逻辑
  async intercept(context: rcp.RequestContext, next: rcp.RequestHandler): Promise<rcp.Response> {
    const response = await next.handle(context);
    const toReturn: rcp.Response = {
      request: response.request,
      statusCode: response.statusCode,
      httpVersion: response.httpVersion,
      headers: {
        'content-range': response.headers['content-range']
      },
      effectiveUrl: response.effectiveUrl,
      timeInfo: response.timeInfo,
      toJSON: () => null
    };
    Logger.info('[ResponseHeaderRemoveInterceptor]: Response was modified');
    return toReturn;
  }
}

说明

NetworkQualityProvider中定义了isNetWorkFast(),isNetWorkFast用于在示例代码中模拟网络质量的好坏,这里仅作为场景模拟,需要开发者自行评估实现。

RequestUrlChangeInterceptor拦截器中,当网络质量较差的时候,修改请求中的URL路径,请求获取分辨率较小的图片,可提升用户体验。

下面将介绍如何使用拦截器,可通过RCP模块中的[SessionConfiguration]来进行设置,在sessionConfig对象中设置interceptors,即可在请求/响应中添加拦截器。

  1. 导入rcp模块、自定义的拦截器。
  2. 设置sessionConfig对象中的interceptors(传入自定义拦截器)。
  3. 调用rcp.createSession()传入sessionConfig,创建通信会话对象session。

核心代码

const sessionConfig: rcp.SessionConfiguration = {
  interceptors: [
    new RequestUrlChangeInterceptor(networkStateSimulator),
    new ResponseHeaderRemoveInterceptor()
  ],
  requestConfiguration:{
    security:{
      tlsOptions: {
        tlsVersion: 'TlsV1.3'
      }
    }
  }
};

const session = rcp.createSession(sessionConfig);

捕获有关HTTP请求/响应流的详细信息

当需要采集应用中HTTP请求的详细跟踪信息时,可以使用[TracingConfiguration]进行相关配置。TracingConfiguration中可以设置verbose(启用详细跟踪)、[infoToCollect](配置要收集的特定类型的信息事件)、collectTimeInfo(在跟踪过程中是否应收集与时间相关的信息)、[httpEventsHandler](为HTTP请求/响应过程中的特定操作定义响应处理程序的回调)四个参数。

下面将以获取HTTP请求/响应时的数据接收时、请求头接收时、数据传输完成时等详细信息为例,进行介绍。

实现思路

通过配置[TracingConfiguration]中的参数,来捕获HTTP请求/响应时的详细信息。

  1. 导入rcp模块。
  2. 设置tracingConfig对象中的verbose为true,表示启用详细跟踪。
  3. 设置tracingConfig对象中的infoToCollect对象中的incomingData为true(收集传入的数据信息事件)、outgoingData为true(收集传出的数据信息事件)、incomingHeader为true(收集传入的header信息事件)、outgoingHeader为true(收集传出的header信息事件)。
  4. 设置tracingConfig对象中collectTimeInfo为true,表示在跟踪过程中收集与时间相关的信息。可在请求的[Response]中的[timeInfo]中查看相关信息。
  5. 在[HttpEventsHandler]中设置onDataReceive(当接收到HTTP响应正文的一部分时调用的回调)、onHeaderReceive(用于在响应期间处理接收到的headers的回调)、onDataEnd(数据传输完成时触发的回调)。
  6. 调用rcp.createSession()传入tracingConfig ,创建通信会话对象session。

核心代码

// 定义自定义响应处理程序
const customHttpEventsHandler: rcp.HttpEventsHandler = {
  onDataReceive: (incomingData: ArrayBuffer) => {
    // 用于处理传入数据的自定义逻辑
    Logger.info('Received data:', JSON.stringify(incomingData));
    return incomingData.byteLength;
  },
  onHeaderReceive: (headers: rcp.RequestHeaders) => {
    // 处理响应头的自定义逻辑
    Logger.info('Received headers:', JSON.stringify(headers));
  },
  onDataEnd: () => {
    // 用于处理数据传输完成的自定义逻辑
    Logger.info('Data transfer complete');
  }
};

// 配置跟踪设置
const tracingConfig: rcp.TracingConfiguration = {
  verbose: true,
  infoToCollect: {
    incomingHeader: true, // 收集传入的header信息事件
    outgoingHeader: true, // 收集传出的header信息事件
    incomingData: true, // 收集传入数据信息事件
    outgoingData: true // 收集传出数据信息事件
  },
  collectTimeInfo: true,
  httpEventsHandler: customHttpEventsHandler
};
const securityConfig: rcp.SecurityConfiguration = {
  tlsOptions: {
    tlsVersion: 'TlsV1.3'
  }
};
// 创建通信会话对象,并传入相关配置
const session = rcp.createSession({ requestConfiguration: { tracing: tracingConfig, security: securityConfig } });
session.get('http://developer.huawei.com').then((response) => {
  Logger.info(`Request succeeded, message is ${JSON.stringify(response)}`);
}).catch((err: BusinessError) => {
  Logger.error(`err: err code is ${err.code}, err message is ${JSON.stringify(err)}`);
});

本文主要介绍了基于RCP实现基础的网络请求、多表单提交、双向证书校验、DNS相关设置、请求与响应拦截、HTTP请求期间捕获详细的跟踪信息等。

  • 调用rcp.createSession()创建通信会话对象,实现基础的网络请求能力。
  • 对标HTTP原生能力,通过new rcp.MultipartForm()可实现多表单提交的场景。
  • 双向证书校验:用于验证服务端和客户端之间的身份和数据完整性,确保通信的安全性。
  • 给HTTP请求配置域名系统(DNS),包括自定义DNS服务器、自定义静态DNS规则和配置HTTPS上的DNS。
  • 定义拦截器对象实现高性能网络资源加载。
  • 使用rcp.TracingConfiguration进行相关配置,在会话中的HTTP请求期间捕获详细的跟踪信息。
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐