当前位置: 首页 > article >正文

鸿蒙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接口可以适应多种不同类型的数据响应。

/**
 * @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;
/**
 * @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
}
/**
 * @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
  }
}
/**
 * @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'
}

http://www.kler.cn/a/451509.html

相关文章:

  • Logback日志框架中的继承机制详解
  • 记一次Maven拉不了包的问题
  • Scala课堂小结
  • Unity复刻胡闹厨房复盘 模块一 新输入系统订阅链与重绑定
  • pyinstaller打包资源文件和ini配置文件怎么放
  • 第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
  • 【电路笔记】-布尔代数真值表
  • 基于springboot的海洋知识服务平台的设计与实现
  • 数据结构与算法作业(五)
  • 【uni-app】2025最新uni-app一键登录保姆级教程(包含前后端获取手机号方法)(超强避坑指南)
  • Pycharm配置PyQt 5
  • Spring Boot 中 Map 的最佳实践
  • dockerfile文档编写(1):基础命令
  • DX12 快速教程(2) —— 渲染天蓝色窗口
  • html + css 淘宝网实战
  • 矩阵方程组求解——Markov过程
  • 黑马Java面试教程_P2_Redis
  • 圆形视频怎么制作?裁剪圆形视频的方法
  • 重温设计模式--单例模式
  • 服务器经常遇到端口被占用的情况怎么办?
  • 《机器学习》流形学习 流形 局部线性嵌入 等距映射(Isomap: 测地线MDS降维
  • 【代码随想录】刷题记录(83)-最大子数组和
  • 利用Java爬虫获取苏宁易购商品详情
  • 决策树(理论知识1)
  • 突发!GitLab将停止对中国区用户提供GitLab.com账号服务
  • 大语言模型中的Agent;常见的Agent开发工具或框架