鸿蒙Next API 12开发,使用@ohos/axios进行HTTP请求

创建了一个名为 HttpRequest 的类,它封装了 axios 的功能,并添加了请求和响应拦截器以处理一些通用的请求和响应逻辑。这个类提供了多种 HTTP 方法(GET、POST、PUT、DELETE)以及用于发送请求并处理响应数据的方法。以下是对您的代码的一些观察和潜在改进建议:

  1. 导入模块

    • 您从 @ohos/axios 导入了 axios。请注意,通常 axios 是从 axios 包本身导入的,而不是从 @ohos/axios。除非您正在使用特定于 OHOS(OpenHarmony)的 axios 版本,否则您可能需要检查这一点。
  2. 请求拦截器

    • 您在请求拦截器中添加了多个自定义头,并生成了 requestId 和签名。这是一个很好的做法,可以增强请求的安全性。
    • 您设置了 validateStatus 函数来允许所有状态码小于 500 的请求通过。这意味着即使服务器返回 4xx 错误,它们也会被视为成功的响应,除非您在响应拦截器中进一步处理它们。
  3. 响应拦截器

    • 您在响应拦截器中记录了响应和错误。这是一个很好的调试工具。
    • 对于错误处理,您检查了 error.response 是否存在,并据此决定如何构建要拒绝的 BusinessError 对象。这是一个合理的做法。
  4. 方法实现

    • 您为不同的 HTTP 方法(GET、POST、PUT、DELETE)提供了单独的包装器方法,这些方法设置了 config.method 并调用了 requestrequestForData。这是一个很好的封装,使得使用这些方法时不需要每次都指定方法类型。
    • postData 方法似乎是多余的,因为它只是调用了 post 方法并解析了 .data 属性。如果 post 方法已经返回了 .data(通过 requestForData),则 postData 可以被移除或重构为直接调用 requestForData
  5. 错误处理

    • 您在 catch 块中处理了错误,并根据错误类型构建了不同的 BusinessError 对象。这是一个很好的做法,因为它允许调用者根据错误类型采取不同的行动。
    • 请注意,如果服务器返回的错误不包含 detailMessagemessage 字段,则您的错误对象可能会包含 undefined 值。您可能需要添加一些默认值或额外的检查来处理这种情况。
  6. 代码重构

    • 考虑将 requestrequestForData 方法中的重复逻辑(如错误处理)提取到单独的私有方法中,以减少代码冗余并提高可维护性。
  7. 类型安全

    • 您已经使用了 TypeScript,这是一个很好的选择,因为它提供了类型安全。确保所有类型定义都是准确的,并且与您的业务逻辑保持一致。
  8. 测试

    • 考虑为您的 HttpRequest 类编写单元测试,以确保其行为符合预期。这可以帮助您在更改代码时捕获潜在的问题。

总的来说,您的代码结构清晰,逻辑合理。通过一些小的改进和重构,您可以进一步提高其健壮性和可维护性。

这段代码定义了几个TypeScript接口,用于描述在Web开发或API交互中可能使用的数据结构。下面是对这些接口的解释:

  1. BaseResponse 接口:

    • code: 一个数字,通常用于表示操作的状态码,例如200表示成功,4xx表示客户端错误,5xx表示服务器错误。
    • detailMessage: 一个字符串,提供关于响应的详细消息或错误描述。
    • message: 一个字符串,提供响应的简短描述或错误消息。
    • status: 一个数字,通常与code相似,用于表示HTTP状态码。
    • success: 一个布尔值,表示操作是否成功。
    • timestamp: 一个字符串,表示响应生成的时间戳。
  2. BaseResponseModel 接口:

    • 这个接口继承自BaseResponse接口,并添加了一个可选的data属性。
    • data?: 一个泛型T,表示响应中携带的数据。由于它是可选的,所以即使没有数据,这个接口也可以被使用。
    • 泛型T允许BaseResponseModel接口用于不同类型的响应数据,提高了接口的灵活性和复用性。
  3. RejectError 接口:

    • 这个接口继承自BaseResponse接口,并添加了三个额外的属性,用于描述一个拒绝或错误的详细情况。
    • path: 一个字符串,表示出错时请求的路径。
    • error: 一个字符串,提供错误的详细描述。
    • requestId: 一个字符串,表示请求的唯一标识符,可以用于日志追踪或调试。

这些接口的设计体现了面向接口编程的思想,使得代码更加模块化、易于维护和扩展。通过定义明确的接口,可以确保不同部分之间的数据交互符合预期,减少了错误和调试的难度。此外,使用泛型T提高了代码的复用性和灵活性,使得BaseResponseModel接口可以适应多种不同类型的数据响应。

ts 复制代码
/**
 * @Title HttpRequest
 * @Description
 * @Author 
 * @create
 */
import axios,
{ AxiosInstance,
  InternalAxiosRequestConfig,
  AxiosRequestConfig,
  AxiosError,
  AxiosResponse
} from '@ohos/axios';
import { Logger } from 'commlib'

import { BaseResponseModel, RejectError } from './Types'
import { NetworkUtil } from './util/NetworkUtil';
import { NetworkConstants } from './constants/NetworkConstants';
import { BusinessError } from '@kit.BasicServicesKit';

const MAX_RETRIES = 3;
const TAG = '[HttpRequest]';

class HttpRequest {
  service: AxiosInstance

  constructor() {
    this.service = axios.create({
      baseURL: 'https://www.baidu.com',
      timeout: 10 * 1000
    });

    // 请求拦截
    this.service.interceptors.request.use(
      async (config: InternalAxiosRequestConfig) => {
        Logger.debug('[Request]', `url=${config.baseURL || ''}${config.url || ''}, params=${JSON.stringify(config.data)}`)

        const requestId = NetworkUtil.makeUUID()
        const timestamp = Date.parse(new Date().toString())

        config.headers['Content-Type'] = 'application/json'
        config.headers['timestamp'] = timestamp
        config.headers['appKey'] = NetworkUtil.getAppKey()
        config.headers['requestId'] = requestId
        config.headers['sign'] = await NetworkUtil.signText(NetworkConstants.API_SECRET + timestamp + requestId)
        config.headers['App-Version'] = NetworkConstants.APP_VERSION
        config.headers['App-Source'] = NetworkConstants.APP_SOURCE
        config.validateStatus = (status) => status < 500
        return config
      },
      (error: AxiosError) => {
        return Promise.reject(error);
      }
    );
    // 响应拦截
    this.service.interceptors.response.use(
      (response: AxiosResponse) => {
        Logger.debug('[Response]', JSON.stringify(response))
        return response
      }, (error: AxiosError) => {
        Logger.debug('[Error]', JSON.stringify(error))
        return Promise.reject(error);
      }
    );
  }

  request<T>(config: AxiosRequestConfig): Promise<BaseResponseModel<T>> {
    return new Promise((resolve: (value: BaseResponseModel<T>) => void, reject: (reason?: BusinessError<RejectError>) => void) => {
      this.service.request<BaseResponseModel<T>>(config)
        .then((response: AxiosResponse<BaseResponseModel<T>>) => {
          resolve(response.data as BaseResponseModel<T>);
        })
        .catch((error: AxiosError) => {
          if (error.response?.data != undefined) {
            // reject(error.response?.data as RejectError)
            let result = error.response?.data as RejectError
            reject({
              code: -1,
              message: result.detailMessage,
              name: result.message,
              data: result,
            });
          } else {
            reject({
              code: -1,
              message: 'AxiosError',
              name: '网络异常',
            })
          }
        })
    });
  }

  requestForData<T>(config: AxiosRequestConfig): Promise<T> {
    return new Promise((resolve: (value: T) => void, reject: (reason?: RejectError) => void) => {
      this.service.request<BaseResponseModel<T>>(config)
        .then((response: AxiosResponse<BaseResponseModel<T>>) => {
          resolve(response.data.data!);
        })
        .catch((error: AxiosError) => {
          reject(error.response?.data as RejectError)
        })
    });
  }

  get<T>(config: AxiosRequestConfig): Promise<BaseResponseModel<T>> {
    config.method = 'GET'
    return this.request<T>(config)
  }
  post<T>(config: AxiosRequestConfig): Promise<BaseResponseModel<T>> {
    config.method = 'POST'
    return this.request<T>(config)
  }
  postForData<T>(config: AxiosRequestConfig): Promise<T> {
    config.method = 'POST'
    return this.requestForData<T>(config)
  }
  put<T>(config: AxiosRequestConfig): Promise<BaseResponseModel<T>> {
    config.method = 'PUT'
    return this.request<T>(config)
  }
  delete<T>(config: AxiosRequestConfig): Promise<BaseResponseModel<T>> {
    config.method = 'DELETE'
    return this.request<T>(config)
  }
  postData<T>(config: AxiosRequestConfig): Promise<T> {
    return new Promise((resolve: (result: T) => void, reject: (error: RejectError) => void) => {
      this.post<T>(config).then((result: BaseResponseModel<T>) => {
        resolve(result.data!)
      }).catch((error: RejectError) => {
        reject(error);
      })
    })
  }
}

const httpRequest = new HttpRequest()
export default httpRequest;
ts 复制代码
/**
 * @Title Types
 * @Description
 * @Author 
 * @create 
 */

export interface RejectError extends BaseResponse {
  path: string
  error: string
  requestId: string
}

export interface BaseResponseModel<T> extends BaseResponse {
  data?: T
}
interface BaseResponse {
  code: number
  detailMessage: string
  message: string
  status: number
  success: boolean
  timestamp: string
}
ts 复制代码
/**
 * @Title NetworkUtil
 * @Description
 * @Author 
 * @create 
 */

import { util } from '@kit.ArkTS'
import { cryptoFramework } from '@kit.CryptoArchitectureKit'
import { NetworkConstants } from '../constants/NetworkConstants'

export class NetworkUtil {
  public static async signText(rawStr: string): Promise<string> {
    let data = new util.TextEncoder().encodeInto(rawStr)

    let sha1 = cryptoFramework.createMd('SHA1')
    await sha1.update({ data: data })
    const dataBlob = await sha1.digest()
    const result = NetworkUtil.uint8ArrayToHexStr(dataBlob.data)

    return result
  }

  private static uint8ArrayToHexStr(data: Uint8Array): string {
    let hexString = "";
    let i: number;
    for (i = 0; i < data.length; i++) {
      let char = ('00' + data[i].toString(16)).slice(-2);
      hexString += char;
    }
    return hexString;
  }

  public static makeUUID(): string {
    const result: string = util.generateRandomUUID(false).replace(/-/g, '')
    return result
  }
  public static getAppKey(): string {
    return NetworkConstants.API_KEY
  }
  public static getAppSecret(): string {
    return NetworkConstants.API_SECRET
  }
}
ts 复制代码
/**
 * @Title NetworkConstants
 * @Description
 * @Author 贾少英
 * @create 2024/3/27
 */
export class NetworkConstants {
  static readonly API_KEY: string = ''
  static readonly API_SECRET: string = ''
  static readonly APP_SOURCE: string = 'HarmonyOS'
  static readonly APP_VERSION: string = '1.0.1'
}
相关推荐
轻口味1 小时前
【每日学点鸿蒙知识】沙箱目录、图片压缩、characteristicsArray、gm-crypto 国密加解密、通知权限
pytorch·华为·harmonyos
xo198820115 小时前
鸿蒙人脸识别
redis·华为·harmonyos
塞尔维亚大汉6 小时前
【OpenHarmony】 鸿蒙 UI开发之CircleIndicator
harmonyos·arkui
BisonLiu6 小时前
华为仓颉鸿蒙HarmonyOS NEXT仓颉原生数据网络HTTP请求(ohos.net.http)
harmonyos
BisonLiu6 小时前
华为仓颉鸿蒙NEXT原生加解密算法库框架
harmonyos
变色龙云6 小时前
网页生成鸿蒙App
华为·harmonyos
BisonLiu6 小时前
华为仓颉鸿蒙HarmonyOS NEXT仓颉原生ohos.request(上传下载)
harmonyos
s_daqing6 小时前
华为手机鸿蒙4.2连接不上adb
华为·智能手机·harmonyos
唐 城6 小时前
curl 放弃对 Hyper Rust HTTP 后端的支持
开发语言·http·rust
Lucky me.6 小时前
鸿蒙开发使用axios请求后端网络服务出现该错误
华为·harmonyos