头图

【高心星出品】

RCP框架的使用

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

RCP vs HTTP

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

功能分类功能名称功能描述HTTPRCP
基础功能发送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.Request

let req = new rcp.Request(url: URLOrString, method?: HttpMethod, headers?: RequestHeaders, content?: RequestContent, cookies?: RequestCookies, transferRange?: TransferRange | TransferRange[], configuration?: Configuration)
参数名类型必填说明
urlURLOrString请求的地址类型,在构造函数中,参数可以是 URLOrString,但 URL 只是 URL,字符串需要转换为URL。
methodHttpMethodHTTP 请求方法。默认是GET。
headersRequestHeadersHTTP请求头。
contentRequestContentHTTP 请求正文。
cookiesRequestCookiesHTTP 请求 cookie。该设置将转换为 HTTP Cookies header。
transferRangeTransferRange\TransferRange[]HTTP 传输范围。该设置将转换为 HTTP Range header。常用于断点续传。
configurationConfigurationHTTP 请求配置。见Configuration。

应答对象rcp.Response

名称类型只读必填说明
requestRequestN/A收到此响应的相关HTTP请求内容。
statusCodenumberN/AHTTP请求的结果代码。如果回调函数成功执行,将返回ResponseCode中定义的结果代码。否则,将在AsyncCallback中的error字段中返回错误代码。
headersResponseHeadersN/A响应头。
effectiveUrlURLN/A重定向后请求的有效URL。默认值为undefined。
bodyArrayBufferN/A响应内容根据响应头中的Content-type返回。响应内容必须与服务器返回的数据类型相同。
downloadedToDownloadedToN/A内容下载的路径。在使用下载至文件对象和文件标识符两种方式时,默认情况下,并未设置回调信息。起始版本:5.0.0(12)
debugInfoDebugInfo[]N/A与响应相关联的调试信息。默认值为undefined。
timeInfoTimeInfoN/AHTTP请求各阶段的定时信息。默认值为undefined。
cookiesResponseCookie[]N/A响应中的Cookie数组。
httpVersionHttpVersionN/AHTTP的版本。起始版本:5.0.0(12)
reasonPhrasestringN/AHTTP响应状态行的reasonPhrase,提供与数字状态代码相关的文本描述。起始版本:5.0.0(12)
toString()方法N/A将应答结果转化为字符串,主要是转化了body的结果
toJson()方法N/A将toString()方法返回的字符串转化为json对象,我们可以进一步处理成自己需要的类型。

简单的应用

该测试应用包含了基本发送get、post请求,编写请求和应答拦截器、下载文件、下载文件流、上传文件、上传文件流等功能的实现。通过编写该案例可以覆盖大部分RCP框架的应用场景。

在这里插入图片描述

Get请求

  • 创建会话对象
  • 创建请求对象
  • 发起请求
let sessionconfig: rcp.SessionConfiguration = {
  //请求服务器基准地址
  baseAddress: 'https://httpbin.org'
}
let session = rcp.createSession(sessionconfig) //创建会话

let req = new rcp.Request('/get?id=10&name=gxx', 'GET') //创建get请求
session.fetch(req).then((res: rcp.Response) => { //发送请求获取应答
  this.message = JSON.stringify(res)
}).catch((e: Error) => {
  this.message = 'gxxt ' + e.message
}).finally(() => {
  session.close() //关闭会话
})

Post请求

  • 创建会话对象
  • 创建请求对象
  • 发起请求
let userinfo: UserType = {
  //post请求携带数据
  id: '10',
  name: 'gxx'
}
let sessionconfig: rcp.SessionConfiguration = {
  //会话配置项
  baseAddress: 'https://httpbin.org'
}
let session = rcp.createSession(sessionconfig) //创建会话
let req = new rcp.Request('/post', 'POST', undefined, userinfo) //创建请求
session.fetch(req).then((res: rcp.Response) => { //发送请求获得应答
  this.message = 'gxxt ' + JSON.stringify(res)
}).catch((e: Error) => {
  this.message = 'gxxt ' + e.message
}).finally(() => {
  session.close() //关闭会话
})

使用拦截器

  • 自定义拦截器

拦截器需要实现rcp.Interceptor接口并重写interceptor方法。

async intercept(context: rcp.RequestContext, next: rcp.RequestHandler): Promise<rcp.Response>

context:包含了请求对象和会话对象,可以获取请求的信息。

next:请求执行器,可以执行context获取应答结果。

Promise<rcp.Response>:返回一个应答结果,可以是next执行的结果也可以是自己创建的应答结果。

下面案例定义一个拦截器,将请求中携带数据修改,并拦截应答结果,只返回部分应答结果。

export class HttpReqInterceptor implements rcp.Interceptor {
  // 拦截方法
  //context 包含所有的请求和会话信息
  //next 下一个拦截器
  async intercept(context: rcp.RequestContext, next: rcp.RequestHandler): Promise<rcp.Response> {
    let url = context.request.url
    if (url.hostname === 'httpbin.org' && url.params.has('id') && context.request.method === 'GET') { //判定地址中半酣httpbin并且携带数据有id
      let id = url.params.get('id')
      url.params.set('id', 'id-' + id) //将id的值替换为id-
    }
    let res=await next.handle(context) //获取应答数据
    let nres:rcp.Response={ //在应答数据的基础上创建了新的应答数据
      request:res.request,
      headers:res.headers,
      statusCode:res.statusCode,
      // 将返回的数据进行了截取只获取args的值
      toJSON:()=> JSON.parse(res.toString()!.substring(res.toString()!.indexOf(':')+1,res.toString()!.indexOf('}')+1).replace('\n',''))
    }
    return nres  //返回了新的应答数据

  }
}
  • 创建会话对象
  • 创建请求对象
  • 发起请求
let sessionconfig: rcp.SessionConfiguration = {
  //请求服务器基准地址
  baseAddress: 'https://httpbin.org',
  interceptors: [//使用自定义的拦截器
    new HttpReqInterceptor()
  ]
}
let session = rcp.createSession(sessionconfig) //创建会话

let req = new rcp.Request('/get?id=10&name=gxx', 'GET') //创建get请求
session.fetch(req).then((res: rcp.Response) => { //发送请求获取应答
 this.message = 'gxxt ' + (res.toJSON() as UserType).name//将应答结果转化为自己定义的类型
}).catch((e: Error) => {
  this.message = 'gxxt ' + e.message
}).finally(() => {
  session.close() //关闭会话
})

下载文件

  • 创建文件保存位置
// 文件保存位置
let downloadfile: rcp.DownloadToFile = {
  kind: 'folder', //目录类型
  path: getContext(this).filesDir //目录的路径
}
  • 创建会话对象
  • 下载文件
// 图片资源地址
let url =
  'https://ts1.cn.mm.bing.net/th/id/R-C.57384e4c2dd256a755578f00845e60af?rik=uy9%2bvT4%2b7Rur%2fA&riu=http%3a%2f%2fimg06file.tooopen.com%2fimages%2f20171224%2ftooopen_sy_231021357463.jpg&ehk=whpCWn%2byPBvtGi1%2boY1sEBq%2frEUaP6w2N5bnBQsLWdo%3d&risl=&pid=ImgRaw&r=0'
// 会话对象
let session: rcp.Session = rcp.createSession()
// 文件保存位置
let downloadfile: rcp.DownloadToFile = {
  kind: 'folder',
  path: getContext(this).filesDir
}
// 开始下载文件
session.downloadToFile(url, downloadfile).then((res) => {
  this.message = 'gxxt a' + JSON.stringify(res)
}).catch((e: Error) => {
  this.message = 'gxxt ' + e.message
}).finally(() => {
  session.close()
})

下载文件流

  • 创建文件写流
let downloadto: rcp.DownloadToStream = {
      kind: 'stream',  //类型为流
      stream: {
        write: (buffer: ArrayBuffer) => { //每次下载的资源保存在buffer中,会执行多次
         let len= fileIo.writeSync(f.fd, buffer) //写入到文件中
          return Promise.resolve(len) //返回写入文件的字节个数
        }
      }
    }
  • 创建会话对象
  • 读取网络资源的字节流
  • 将该字节流保存在设备上

    // 图片资源地址
    let url =
      'https://ts1.cn.mm.bing.net/th/id/R-C.57384e4c2dd256a755578f00845e60af?rik=uy9%2bvT4%2b7Rur%2fA&riu=http%3a%2f%2fimg06file.tooopen.com%2fimages%2f20171224%2ftooopen_sy_231021357463.jpg&ehk=whpCWn%2byPBvtGi1%2boY1sEBq%2frEUaP6w2N5bnBQsLWdo%3d&risl=&pid=ImgRaw&r=0'
    let session: rcp.Session = rcp.createSession() //会话对象
    let file = getContext(this).filesDir + '/test2.jpg' //文件保存路径
    let f: fileIo.File = fileIo.openSync(file, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY) //文件对象
    let downloadto: rcp.DownloadToStream = {
      kind: 'stream',
      stream: {
        write: (buffer: ArrayBuffer) => { //每次下载的资源保存在buffer中,会执行多次
         let len= fileIo.writeSync(f.fd, buffer) //写入到文件中
          return Promise.resolve(len) //返回写入文件的字节个数
        }
      }
    }
    session.downloadToStream(url, downloadto).then((res: rcp.Response) => { //下载文件成字节流
      this.message = 'gxxt downloadtostream ' + JSON.stringify(res)
    }).catch((e: Error) => {
      this.message = 'gxxt ' + e.message
    }).finally(() => {
      session.close()
    })

上传文件

  • 要上传的文件对象
// 文件地址
    let uploadfromfile: rcp.UploadFromFile = {
      fileOrPath: getContext(this).filesDir + '/test1.jpg'
    }
  • 创建会话对象
  • 上传文件请求
// 上传地址
let url = 'https://httpbin.org/post'
// 会话对象
let session = rcp.createSession()
// 文件地址
let uploadfromfile: rcp.UploadFromFile = {
  fileOrPath: getContext(this).filesDir + '/test1.jpg'
}
// 开始上传
session.uploadFromFile(url, uploadfromfile).then((res: rcp.Response) => {
  this.message = 'gxxt ' + JSON.stringify(res)
}).catch((e: Error) => {
  this.message = 'gxxt ' + e.message
}).finally(() => {
  session.close()
})

上传文件流

  • 要上传文件对象读流
let uploadstream: rcp.UploadFromStream = {
      stream: {
        read: (buffer: ArrayBuffer) => { //每次将文件的字节读入buffer 等待上传到服务器
          let len = fileIo.readSync(file.fd, buffer)
          console.log('gxxt 读了:', len)
          return Promise.resolve(len)
        }
      }
    }
  • 创建会话对象
  • 上传文件对象流
// 文件上传地址
let url = 'https://httpbin.org/post'
// 创建会话对象
let session = rcp.createSession()
// 需要上传的文件
let file: fileIo.File = fileIo.openSync(getContext(this).filesDir + '/test1.jpg', fileIo.OpenMode.READ_ONLY)
let uploadstream: rcp.UploadFromStream = {
  stream: {
    read: (buffer: ArrayBuffer) => { //每次将文件的字节读入buffer 等待上传到服务器
      let len = fileIo.readSync(file.fd, buffer)
      console.log('gxxt 读了:', len)
      return Promise.resolve(len)
    }
  }
}
session.uploadFromStream(url, uploadstream).then((res: rcp.Response) => { //开始上传
  this.message = 'gxxt ' + JSON.stringify(res)
}).catch((e: Error) => {
  this.message = 'gxxt ' + e.message
}).finally(() => {
  session.close()
})

高心星
1 声望1 粉丝

华为开发者专家(HDE)。 10年教学经验,兼任多家科技公司技术顾问。先后从事JavaEE项目开发、Python爬虫、HarmonyOS移动应用开发等课程的教学工作。参与开发《鸿蒙应用开发基础》和《鸿蒙项目实战》等课程。