WDHttp.ets 10.9 KB
import http from '@ohos.net.http';
import { BusinessError } from '@ohos.base';
import ArrayList from '@ohos.util.ArrayList';
import {Logger} from 'wdKit';
import { HttpUtils } from '../utils/HttpUtils';

const TAG = 'WDHttp';

export namespace WDHttp {

  export enum RequestMethod {
    OPTIONS = http.RequestMethod.OPTIONS,
    GET = http.RequestMethod.GET,
    HEAD = http.RequestMethod.HEAD,
    POST = http.RequestMethod.POST,
    PUT = http.RequestMethod.PUT,
    DELETE = http.RequestMethod.DELETE,
    TRACE = http.RequestMethod.TRACE,
    CONNECT = http.RequestMethod.CONNECT,
  }

  export enum HttpParamsType {
    STRING,
    OBJECT,
    ARRAY_BUFFER
  }

  export interface RequestOptions {
    // 请求方法
    method?: RequestMethod;

    // 请求头
    header?: object;

    // 读超时时间 单位s 默认60
    readTimeout?: number;

    // 连接超时时间 单位s 默认60
    connectTimeout?: number;

    // 请求参数 当请求为get请求时 该参数会自动拼接在url后面
    params?: object;

    // 请求参数类型 当请求为post时有效
    paramsType?: HttpParamsType;
  }

  export const enum ErrorCode {
    // 请求执行(查询)失败
    FAILED = -1,
    // 请求执行(查询)成功
    SUCCESS = http.ResponseCode.OK,
    // 业务错误码。。。
  }

  /**
   * ResponseDTO
   * 微服务通用返回结构
   * http://confluence.cmvideo.cn/confluence/pages/viewpage.action?pageId=4232418
   */
  export interface ResponseDTO<T = string> {
    // 服务请求响应值/微服务响应状态码”
    code: number;

    // 服务请求响应说明
    message: string;

    // 响应结果
    body?: T;

    // 请求响应时间戳(unix格式)
    timeStamp?: number;
    // timestamp?: number;

    // 返回当前时间戳,格式:yyyyMMddHHmmss,如20230918102124
    dataVersion?: string;
  }

  /**
   * ResponseVO
   * 基础服务通用返回结构
   * 如PUGC错误码:http://confluence.cmvideo.cn/confluence/pages/viewpage.action?pageId=168081524
   */
  export interface ResponseVO<T = string> {
    // 返回代码,参考附录ResultCode 即(ACCEPTED/SUCCESS/FAILED/REJECTED等)
    resultCode: string;

    // 返回描述
    resultDesc: string;

    // 错误码,参考附录和接口中ErrorCode(当resultCode=FAILED才有),如:"ERR_USER_NOTFOUND"
    errorCode?: string;

    // 业务数据
    data?: T;
  }

  /**
   * The Response ResultCode enum.
   */
  export const enum ResultCode {
    // 请求执行(查询)成功
    SUCCESS = 'SUCCESS',
    // 请求执行(查询)失败
    FAILED = 'FAILED',
    // 该请求被拒绝处理,例如参数错误或非法等
    REJECTED = 'REJECTED',
    // 该请求已经被接收,但是本次同步返回无法知道执行结果
    ACCEPTED = 'ACCEPTED',
    // 参数错误
    // ERR_NOT_VALID_PARAMETERS = 'ERR_NOT_VALID_PARAMETERS'
  }

  export class Request {
    private static globalHeaderProviders: ArrayList<() => Record<string, string>> = new ArrayList();

    static addGlobalHeaderProvider(provider: () => Record<string, string>) {
      Request.globalHeaderProviders.add(provider)
    }

    static initHttpHeader() {
      Request.addGlobalHeaderProvider(() => {
        return HttpUtils.buildHeaders();
      })
    }

    private static makeRequestOptions(options?: RequestOptions): http.HttpRequestOptions {
      if (options) {
        let op: http.HttpRequestOptions = {};
        op.method = options.method!.toString() as http.RequestMethod;
        op.header = options.header
        op.readTimeout = (options.readTimeout ?? 60) * 1000
        op.connectTimeout = (options.connectTimeout ?? 60) * 1000
        if (options.method == RequestMethod.POST) {
          op.extraData = options.params;
          if (options.paramsType == HttpParamsType.STRING) {
            op.expectDataType = http.HttpDataType.STRING;
          } else if (options.paramsType == HttpParamsType.OBJECT) {
            op.expectDataType = http.HttpDataType.OBJECT;
          } else if (options.paramsType == HttpParamsType.ARRAY_BUFFER) {
            op.expectDataType = http.HttpDataType.ARRAY_BUFFER;
          }
        } else if (options.method == RequestMethod.PUT) {
          // todo
        } else if (options.method == RequestMethod.DELETE) {
          // todo
        }
        return op;
      }
      return {}
    }

    private static makeUrl(url: string, options?: RequestOptions): string {
      if (!options || (options.method && options.method != RequestMethod.GET)) {
        return url;
      }
      if (!options.params) {
        return url;
      }
      let paramStr = "";
      if (typeof options.params == "string") {
        paramStr = options.params;
      } else if (typeof options.params == "object") {
        let arr: string[] = [];
        Object.entries(options.params).forEach((entry: object) => {
          arr.push(`${entry[0]}=${entry[1]}`)
        })
        if (arr.length == 0) {
          return url;
        }
        paramStr = arr.join("&")
      }
      if (url.indexOf("?") == -1) {
        return url + "?" + paramStr;
      } else {
        if (url.endsWith("?")) {
          return url + paramStr;
        } else {
          return url + "&" + paramStr;
        }
      }
    }

    // 加入泛型限定,返回数据类型为T,
    // static request<T = any>(url: string, options: RequestOptions, callback?: AsyncCallback<ResponseDTO<T>>) {
    //   let httpRequest = http.createHttp();
    //   if (!options) {
    //     options = {};
    //   }
    //   if (!options.method) {
    //     options.method = RequestMethod.GET;
    //   }
    //   if (!options.header) {
    //     let header: Record<string, string> = {
    //       // 'Content-Type': 'text/plain'
    //       'Content-Type': 'application/json;charset=utf-8'
    //     };
    //     options.header = header
    //   } else if (!options.header['Content-Type']) {
    //     options.header['Content-Type'] = 'application/json;charset=utf-8';
    //   }
    //
    //   Request.globalHeaders.forEach((value, key) => {
    //     options!.header![key] = value;
    //   })
    //   let realUrl = Request.makeUrl(url, options);
    //   httpRequest.request(realUrl, Request.makeRequestOptions(options), (error, responseData: http.HttpResponse) => {
    //     if (callback) {
    //       if (error) {
    //         callback(error, undefined)
    //       } else {
    //         try {
    //           let response: ResponseDTO<T> = JSON.parse(`${responseData.result}`)
    //           callback(error, response)
    //         } catch (err) {
    //           callback(error, { code: responseData?.responseCode, message: responseData?.result?.toString() })
    //         }
    //       }
    //     }
    //   })
    // }
    //
    // static get<T = any>(url: string, options: RequestOptions, callback?: AsyncCallback<ResponseDTO<T>>) {
    //   let op = options ?? {}
    //   op.method = RequestMethod.GET;
    //   Request.request<T>(url, op, callback)
    // }
    //
    // static post<T = any>(url: string, options: RequestOptions, callback?: AsyncCallback<ResponseDTO<T>>) {
    //   let op = options ?? {}
    //   op.method = RequestMethod.POST;
    //   Request.request<T>(url, op, callback)
    // }
    //
    // static put<T = any>(url: string, options: RequestOptions, callback?: AsyncCallback<ResponseDTO<T>>) {
    //   let op = options ?? {}
    //   op.method = RequestMethod.PUT;
    //   Request.request<T>(url, op, callback)
    // }
    //
    // static delete<T = any>(url: string, options: RequestOptions, callback?: AsyncCallback<ResponseDTO<T>>) {
    //   let op = options ?? {}
    //   op.method = RequestMethod.DELETE;
    //   Request.request<T>(url, op, callback)
    // }
    ////
    static request<T = string>(url: string, options?: RequestOptions): Promise<T> {
      let httpRequest = http.createHttp();
      if (!options) {
        options = {};
      }
      if (!options.method) {
        options.method = RequestMethod.GET;
      }
      if (!options.header) {
        let header: Record<string, string> = {
          // 'Content-Type': 'text/plain'
          'Content-Type': 'application/json;charset=utf-8'
        };
        options.header = header
      } else if (!options.header['Content-Type']) {
        options.header['Content-Type'] = 'application/json;charset=utf-8';
      }
      let commonHeader: Record<string, string | number>  = { };
      Request.globalHeaderProviders.forEach((func) => {
        let headers = func();
        for (const obj of Object.entries(headers)) {
          commonHeader[obj[0]] = obj[1];
        }
      })
      let userHeader = options.header as Record<string, string | number>
      for (const obj1 of Object.entries(userHeader)) {
        commonHeader[obj1[0]] = obj1[1];
      }
      options.header = commonHeader
      let realUrl = Request.makeUrl(url, options);
      Logger.info(TAG, `request realUrl: ${realUrl}`);
      Logger.info(TAG, `header: ${realUrl}`);
      return new Promise<T>((resolve, reject) => {
        httpRequest.request(realUrl, Request.makeRequestOptions(options))
          .then((responseData: http.HttpResponse) => {
            try {
              if (responseData.responseCode == http.ResponseCode.OK) {
                // todo:待处理JSON.parse只解析目标class中指定的字段
                // let response = JSON.parse(`${data}`) as ResponseDTO<T>
                let response: T = JSON.parse(`${responseData.result}`)
                resolve(response)
              } else {
                // 返回码不是ok
                let responseError: BusinessError = {
                  code: responseData?.responseCode,
                  message: responseData?.result?.toString(),
                  name: '业务错误',
                }
                reject(responseError)
              }
            } catch (err) {
              // json解析异常
              let responseParseError: BusinessError = {
                code: responseData?.responseCode,
                message: responseData?.result?.toString(),
                name: '解析异常',
              }
              reject(responseParseError)
            }
          })
          .catch((error: BusinessError) => {
            reject(error)
          })
      })
    }

    static get<T = ResponseDTO<string>>(url: string, options?: RequestOptions): Promise<T> {
      let op = options ?? {}
      op.method = RequestMethod.GET;
      return Request.request<T>(url, op)
    }

    static post<T = ResponseDTO<string>>(url: string, options?: RequestOptions): Promise<T> {
      let op = options ?? {}
      op.method = RequestMethod.POST;
      return Request.request<T>(url, op)
    }

    static put<T = ResponseDTO<string>>(url: string, options?: RequestOptions): Promise<T> {
      let op = options ?? {}
      op.method = RequestMethod.PUT;
      return Request.request<T>(url, op)
    }

    static delete<T = ResponseDTO<string>>(url: string, options?: RequestOptions): Promise<T> {
      let op = options ?? {}
      op.method = RequestMethod.DELETE;
      return Request.request<T>(url, op)
    }
  }
}