Skip to content

asiox

添加网络权限

  • module.json5中添加网络权限:
json
{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      }
    ]
  }
}

安装

  • 点击底部终端 输入命令
ts
ohpm install @ohos/axios

封装 axios

创建网络请求工具文件 ets/utils/request.ets

利用 axios 的 create 方法创建 axios 实例

  • 配置我们全局公共的网络请求前缀 baseURL。

  • 配置统一的超时时间 timeout

  • 配置公共的统一 headers。

ts
import axios, {
  InternalAxiosRequestConfig,
  AxiosResponse,
  AxiosError,
  AxiosRequestConfig,
  AxiosInstance,
} from "@ohos/axios";
// 创建实例
const instance: AxiosInstance = axios.create({
  baseURL: "http://XXX.XX.XX.XX", //修改为自己项目的实际地址
  timeout: 10000, //超时
  headers: { "Content-Type": "application/json" },

  // `transformRequest` 允许在向服务器发送前,修改请求数据 一般来说用不到,只要后端同志是正常的
  // 它只能用于 'PUT', 'POST' 和 'PATCH' 这几个请求方法
  // 数组中最后一个函数必须返回一个字符串, 一个Buffer实例,ArrayBuffer,FormData,或 Stream
  // 你可以修改请求头。
  // transformRequest: [(data: ESObject, headers: AxiosRequestHeaders) => {
  //   // 对发送的 data 进行任意转换处理
  //   // console.log('api1Result出零四',data)
  //   // return data
  // }],
});

配置添加请求拦截器

ts
// 请求拦截器
instance.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // 对请求数据做点什么
    const token = "eyJhbw"; //自己根据自己项目实际情况获取
    if (token) {
      config.headers["token"] = token; //设置token
    }
    return config;
  },
  (error: AxiosError) => {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

配置添加响应拦截器

下面内容用到了@ohos.router 进行页面 跳转,用到了@kit.ArkUI'promptAction.showToast 进行统一错误提示。 响应拦截非常重要,他是我们封装请求好用的关键,但是每个项目业务不同,其实响应拦截的配置都有不小的差异,但是整体思路是一致的,所以要根据自己的实际情况进行修改。

ts
// 添加响应拦截器
instance.interceptors.response.use(
  (response: AxiosResponse) => {
    // 对响应数据做点什么
    // 因为 我们后台返回的 数据结构是统一的例如{code:2001,massage:'用户信息不能为空',data:{}}
    // 所以下面配置根据系统返回来配置的,不同的系统配置不同
    if (response.data.code == "2001") {
      //这里是举例 要根据自己项目的实际情况进行处理
      promptAction.showToast({
        //用到了 @kit.ArkUI 的 promptAction进行系统弹窗提示
        message: response.data.massage,
        duration: 2000,
        alignment: Alignment.Center,
      });
      return Promise.reject(response.data);
    }
    if (response.data.code == "4001") {
      //这里是举例 要根据自己项目的实际情况进行处理
      router.pushUrl({
        //用到了 @ohos.router 进行页面跳转
        url: "pages/LoginPage",
      });
      return Promise.reject(response.data);
    }
    return response.data;
  },
  (error: AxiosError) => {
    console.log("AxiosError", JSON.stringify(error.response));
    const status = error.response?.status;
    switch (status) {
      case 401: //无权限未登录
        router.pushUrl({
          //用到了 @ohos.router 进行页面跳转
          url: "pages/LoginPage",
        });
        break;
      default:
        promptAction.showToast({
          //用到了 @kit.ArkUI 的 promptAction进行系统弹窗提示
          message: "系统异常,请稍后再试!",
          duration: 2000,
          alignment: Alignment.Center,
        });
        break;
    }
    return Promise.reject(error);
  }
);

使用泛型 T 暴露我们请求的配置

在本例中我针对后端返回的数据如下:

ts
{
    "code": 2001,
    "message": "接口调用失败",
    "data": { } //data的内容不是固定的可能是null,可能是对象,也可能是数组
}

因为我们接口返回结果不固定,所以我们利用一下 Ts 的 泛型暴露我们封装的请求。

完整的封装

ts
import axios, {
  InternalAxiosRequestConfig,
  AxiosResponse,
  AxiosError,
  AxiosRequestConfig,
  AxiosInstance,
} from "@ohos/axios";
import router from "@ohos.router";
import { promptAction } from "@kit.ArkUI";

interface ApiResponse<T> {
  //根据项目实际项目修改
  data: T;
  message?: string;
  code?: number;
}
// 创建实例
const instance: AxiosInstance = axios.create({
  baseURL: "http://XXX.XXX.XXX", //修改为自己项目的实际地址
  timeout: 10000, //超时
  headers: { "Content-Type": "application/json" },
});

//暴露封装请求--为什么没有把上面实例放入到下面中,避免每一个接口都去创建一次
//这里利用了泛型 T
export default <T>(config: AxiosRequestConfig): Promise<ApiResponse<T>> => {
  // 请求拦截器
  instance.interceptors.request.use(
    (config: InternalAxiosRequestConfig) => {
      // 对请求数据做点什么
      const token = "eyJhbGciOiJ...."; //自己根据自己项目实际情况获取我大部分用的 @tencent/mmkv 插件
      if (token) {
        config.headers["authorization"] = token; //设置token
      }
      return config;
    },
    (error: AxiosError) => {
      // 对请求错误做些什么
      return Promise.reject(error);
    }
  );

  // 添加响应拦截器
  instance.interceptors.response.use(
    (response: AxiosResponse) => {
      // 对响应数据做点什么
      // 因为 我们后台返回的 数据结构是统一的例如{code:2001,massage:'用户信息不能为空',data:{}}
      // 所以下面配置根据系统返回来配置的,不同的系统配置不同
      if (response.data.code == "2001") {
        //这里是举例 要根据自己项目的实际情况进行处理
        promptAction.showToast({
          //用到了 @kit.ArkUI 的 promptAction进行系统弹窗提示
          message: response.data.massage,
          duration: 2000,
          alignment: Alignment.Center,
        });
        return Promise.reject(response.data);
      }
      if (response.data.code == "4001") {
        //这里是举例 要根据自己项目的实际情况进行处理
        router.pushUrl({
          //用到了 @ohos.router 进行页面跳转
          url: "pages/LoginPage",
        });
        return Promise.reject(response.data);
      }
      return response.data;
    },
    (error: AxiosError) => {
      console.log("AxiosError", JSON.stringify(error.response));
      const status = error.response?.status;
      switch (status) {
        case 401: //无权限未登录
          router.pushUrl({
            //用到了 @ohos.router 进行页面跳转
            url: "pages/LoginPage",
          });
          break;
        default:
          promptAction.showToast({
            //用到了 @kit.ArkUI 的 promptAction进行系统弹窗提示
            message: "系统异常,请稍后再试!",
            duration: 2000,
            alignment: Alignment.Center,
          });
          break;
      }
      return Promise.reject(error);
    }
  );

  return instance(config);
};

代码示例示范统一管理接口配置(测试)

ets/api/index.ets 文件添加如下内容

ts
import request from "../utils/request";

interface LoginParams {
  phone: string;
  code: string | number;
}

//测试登录
export function TestLogin<T>(data: LoginParams) {
  return request<T>({
    url: "/test/login",
    method: "post",
    data,
  });
}

//测试信息列表
export function parkList<T>(params: Record<string, string>) {
  return request<T>({
    url: "/test/parks",
    method: "get",
    params,
  });
}

代码示例示范调用(测试)

ts

import {TestLogin } from "../api/index"
// 定义返回内容
interface ResponseLogin{
  code:string,
  result:string,
  data:string
}
@Entry
@Component
struct Index {

  async setTestLogin(){
    const result = await TestLogin<ResponseLogin>({phone:'13111111',code:352})
    if(result){
      console.log('result',JSON.stringify(result))
      console.log('code',JSON.stringify(result.code))
      console.log('data',JSON.stringify(result.data))
    }
  }

  build() {
    RelativeContainer() {
      Button('测试').onClick(()=>{
        this.setTestLogin();
      })
    }
    .height('100%')
    .width('100%')
  }
}

补充

就是不要用 .ets后缀结尾。 可以 声明一个 type.d.ts 文件,然后再其他.ets 文件中引入是不会报错的。

也可以用下面的 类型去代替泛型 T。

ts
//嵌套未知类型
export interface PromiseObject {
  [key: string]:
    | string
    | number
    | object
    | boolean
    | undefined
    | PromiseObject
    | PromiseObject[]
    | Array<string | number | boolean | PromiseObject | any>;
}

// any 类型
export type anyType = any;