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

打印 上一主题 下一主题

主题 695|帖子 695|积分 2085

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

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

  • 导入模块

    • 您从 @ohos/axios 导入了 axios。请留意,通常 axios 是从 axios 包自己导入的,而不是从 @ohos/axios。除非您正在利用特定于 OHOS(OpenHarmony)的 axios 版本,否则您可能需要检查这一点。

  • 请求拦截器

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

  • 响应拦截器

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

  • 方法实现

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

  • 错误处理

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

  • 代码重构

    • 思量将 request 和 requestForData 方法中的重复逻辑(如错误处理)提取到单独的私有方法中,以淘汰代码冗余并提高可维护性。

  • 类型安全

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

  • 测试

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

总的来说,您的代码布局清晰,逻辑合理。通过一些小的改进和重构,您可以进一步提高其健壮性和可维护性。
这段代码定义了几个TypeScript接口,用于形貌在Web开发或API交互中可能利用的数据布局。下面是对这些接口的解释:

  • BaseResponse 接口:

    • code: 一个数字,通常用于体现操作的状态码,比方200体现成功,4xx体现客户端错误,5xx体现服务器错误。
    • detailMessage: 一个字符串,提供关于响应的具体消息或错误形貌。
    • message: 一个字符串,提供响应的简短形貌或错误消息。
    • status: 一个数字,通常与code相似,用于体现HTTP状态码。
    • success: 一个布尔值,体现操作是否成功。
    • timestamp: 一个字符串,体现响应生成的时间戳。

  • BaseResponseModel 接口:

    • 这个接口继续自BaseResponse接口,并添加了一个可选的data属性。
    • data?: 一个泛型T,体现响应中携带的数据。由于它是可选的,以是即使没有数据,这个接口也可以被利用。
    • 泛型T允许BaseResponseModel接口用于不同类型的响应数据,提高了接口的机动性和复用性。

  • RejectError 接口:

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

这些接口的筹划体现了面向接口编程的思想,使得代码更加模块化、易于维护和扩展。通过定义明白的接口,可以确保不同部分之间的数据交互符合预期,淘汰了错误和调试的难度。此外,利用泛型T提高了代码的复用性和机动性,使得BaseResponseModel接口可以顺应多种不同类型的数据响应。
  1. /**
  2. * @Title HttpRequest
  3. * @Description
  4. * @Author
  5. * @create
  6. */
  7. import axios,
  8. { AxiosInstance,
  9.   InternalAxiosRequestConfig,
  10.   AxiosRequestConfig,
  11.   AxiosError,
  12.   AxiosResponse
  13. } from '@ohos/axios';
  14. import { Logger } from 'commlib'
  15. import { BaseResponseModel, RejectError } from './Types'
  16. import { NetworkUtil } from './util/NetworkUtil';
  17. import { NetworkConstants } from './constants/NetworkConstants';
  18. import { BusinessError } from '@kit.BasicServicesKit';
  19. const MAX_RETRIES = 3;
  20. const TAG = '[HttpRequest]';
  21. class HttpRequest {
  22.   service: AxiosInstance
  23.   constructor() {
  24.     this.service = axios.create({
  25.       baseURL: 'https://www.baidu.com',
  26.       timeout: 10 * 1000
  27.     });
  28.     // 请求拦截
  29.     this.service.interceptors.request.use(
  30.       async (config: InternalAxiosRequestConfig) => {
  31.         Logger.debug('[Request]', `url=${config.baseURL || ''}${config.url || ''}, params=${JSON.stringify(config.data)}`)
  32.         const requestId = NetworkUtil.makeUUID()
  33.         const timestamp = Date.parse(new Date().toString())
  34.         config.headers['Content-Type'] = 'application/json'
  35.         config.headers['timestamp'] = timestamp
  36.         config.headers['appKey'] = NetworkUtil.getAppKey()
  37.         config.headers['requestId'] = requestId
  38.         config.headers['sign'] = await NetworkUtil.signText(NetworkConstants.API_SECRET + timestamp + requestId)
  39.         config.headers['App-Version'] = NetworkConstants.APP_VERSION
  40.         config.headers['App-Source'] = NetworkConstants.APP_SOURCE
  41.         config.validateStatus = (status) => status < 500
  42.         return config
  43.       },
  44.       (error: AxiosError) => {
  45.         return Promise.reject(error);
  46.       }
  47.     );
  48.     // 响应拦截
  49.     this.service.interceptors.response.use(
  50.       (response: AxiosResponse) => {
  51.         Logger.debug('[Response]', JSON.stringify(response))
  52.         return response
  53.       }, (error: AxiosError) => {
  54.         Logger.debug('[Error]', JSON.stringify(error))
  55.         return Promise.reject(error);
  56.       }
  57.     );
  58.   }
  59.   request<T>(config: AxiosRequestConfig): Promise<BaseResponseModel<T>> {
  60.     return new Promise((resolve: (value: BaseResponseModel<T>) => void, reject: (reason?: BusinessError<RejectError>) => void) => {
  61.       this.service.request<BaseResponseModel<T>>(config)
  62.         .then((response: AxiosResponse<BaseResponseModel<T>>) => {
  63.           resolve(response.data as BaseResponseModel<T>);
  64.         })
  65.         .catch((error: AxiosError) => {
  66.           if (error.response?.data != undefined) {
  67.             // reject(error.response?.data as RejectError)
  68.             let result = error.response?.data as RejectError
  69.             reject({
  70.               code: -1,
  71.               message: result.detailMessage,
  72.               name: result.message,
  73.               data: result,
  74.             });
  75.           } else {
  76.             reject({
  77.               code: -1,
  78.               message: 'AxiosError',
  79.               name: '网络异常',
  80.             })
  81.           }
  82.         })
  83.     });
  84.   }
  85.   requestForData<T>(config: AxiosRequestConfig): Promise<T> {
  86.     return new Promise((resolve: (value: T) => void, reject: (reason?: RejectError) => void) => {
  87.       this.service.request<BaseResponseModel<T>>(config)
  88.         .then((response: AxiosResponse<BaseResponseModel<T>>) => {
  89.           resolve(response.data.data!);
  90.         })
  91.         .catch((error: AxiosError) => {
  92.           reject(error.response?.data as RejectError)
  93.         })
  94.     });
  95.   }
  96.   get<T>(config: AxiosRequestConfig): Promise<BaseResponseModel<T>> {
  97.     config.method = 'GET'
  98.     return this.request<T>(config)
  99.   }
  100.   post<T>(config: AxiosRequestConfig): Promise<BaseResponseModel<T>> {
  101.     config.method = 'POST'
  102.     return this.request<T>(config)
  103.   }
  104.   postForData<T>(config: AxiosRequestConfig): Promise<T> {
  105.     config.method = 'POST'
  106.     return this.requestForData<T>(config)
  107.   }
  108.   put<T>(config: AxiosRequestConfig): Promise<BaseResponseModel<T>> {
  109.     config.method = 'PUT'
  110.     return this.request<T>(config)
  111.   }
  112.   delete<T>(config: AxiosRequestConfig): Promise<BaseResponseModel<T>> {
  113.     config.method = 'DELETE'
  114.     return this.request<T>(config)
  115.   }
  116.   postData<T>(config: AxiosRequestConfig): Promise<T> {
  117.     return new Promise((resolve: (result: T) => void, reject: (error: RejectError) => void) => {
  118.       this.post<T>(config).then((result: BaseResponseModel<T>) => {
  119.         resolve(result.data!)
  120.       }).catch((error: RejectError) => {
  121.         reject(error);
  122.       })
  123.     })
  124.   }
  125. }
  126. const httpRequest = new HttpRequest()
  127. export default httpRequest;
复制代码
  1. /**
  2. * @Title Types
  3. * @Description
  4. * @Author
  5. * @create
  6. */
  7. export interface RejectError extends BaseResponse {
  8.   path: string
  9.   error: string
  10.   requestId: string
  11. }
  12. export interface BaseResponseModel<T> extends BaseResponse {
  13.   data?: T
  14. }
  15. interface BaseResponse {
  16.   code: number
  17.   detailMessage: string
  18.   message: string
  19.   status: number
  20.   success: boolean
  21.   timestamp: string
  22. }
复制代码
  1. /**
  2. * @Title NetworkUtil
  3. * @Description
  4. * @Author
  5. * @create
  6. */
  7. import { util } from '@kit.ArkTS'
  8. import { cryptoFramework } from '@kit.CryptoArchitectureKit'
  9. import { NetworkConstants } from '../constants/NetworkConstants'
  10. export class NetworkUtil {
  11.   public static async signText(rawStr: string): Promise<string> {
  12.     let data = new util.TextEncoder().encodeInto(rawStr)
  13.     let sha1 = cryptoFramework.createMd('SHA1')
  14.     await sha1.update({ data: data })
  15.     const dataBlob = await sha1.digest()
  16.     const result = NetworkUtil.uint8ArrayToHexStr(dataBlob.data)
  17.     return result
  18.   }
  19.   private static uint8ArrayToHexStr(data: Uint8Array): string {
  20.     let hexString = "";
  21.     let i: number;
  22.     for (i = 0; i < data.length; i++) {
  23.       let char = ('00' + data[i].toString(16)).slice(-2);
  24.       hexString += char;
  25.     }
  26.     return hexString;
  27.   }
  28.   public static makeUUID(): string {
  29.     const result: string = util.generateRandomUUID(false).replace(/-/g, '')
  30.     return result
  31.   }
  32.   public static getAppKey(): string {
  33.     return NetworkConstants.API_KEY
  34.   }
  35.   public static getAppSecret(): string {
  36.     return NetworkConstants.API_SECRET
  37.   }
  38. }
复制代码
  1. /**
  2. * @Title NetworkConstants
  3. * @Description
  4. * @Author 贾少英
  5. * @create 2024/3/27
  6. */
  7. export class NetworkConstants {
  8.   static readonly API_KEY: string = ''
  9.   static readonly API_SECRET: string = ''
  10.   static readonly APP_SOURCE: string = 'HarmonyOS'
  11.   static readonly APP_VERSION: string = '1.0.1'
  12. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

李优秀

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表