React Ts 项目配置 Axios

4,325 阅读2分钟

React Hooks Ts Axios

React项目Ts版,使用Hooks API。本示例主要是配置Axios并使用Eggjs写了两个接口进行测试。

主要看 ./src/api./src/model 目录。

├─ src
│  ├─ api
│  │  ├─ api.ts
│  │  └─ status.ts
│  ├─ model
│  │  └─ login.ts

./src/api/api.ts

import axios, {AxiosInstance, AxiosRequestConfig, AxiosResponse} from 'axios';
import qs from 'qs'

import { showMessage } from "./status";
import { message, Spin } from 'antd';
import { ILogin, IUser } from 'model/login';


// 返回res.data的interface
export interface IResponse {
  code: number | string;
  data: any;
  msg: string;
}

let axiosInstance:AxiosInstance = axios.create({
  baseURL: process.env.REACT_APP_BASE_URL,
  headers: {
    Accept: "application/json",
    "Content-Type": "application/x-www-form-urlencoded"
  },
  transformRequest: [
    function(data) {
      //由于使用的 form-data传数据所以要格式化
      delete data.Authorization;
      data = qs.stringify(data);
      return data;
    }
  ]
});

// axios实例拦截响应
axiosInstance.interceptors.response.use(
  (response: AxiosResponse) => {
    if (response.headers.authorization) {
      localStorage.setItem('app_token', response.headers.authorization);
    } else {
      if (response.data && response.data.token) {
        localStorage.setItem('app_token', response.data.token);
      }
    }

    if (response.status === 200) {
      return response;
    } else {
      showMessage(response.status);
      return response;
    }
  },
  // 请求失败
  (error: any) => {
    const {response} = error;
    if (response) {
      // 请求已发出,但是不在2xx的范围
      showMessage(response.status);
      return Promise.reject(response.data);
    } else {
      message.error('网络连接异常,请稍后再试!');
    }
  }
);

// axios实例拦截请求
axiosInstance.interceptors.request.use(
  (config: AxiosRequestConfig) => {
    const token = localStorage.getItem('app_token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    return config;
  },
  (error:any) => {
    return Promise.reject(error);
  }
)  

/**
 * @description: 用户登录
 * @params {ILogin} params
 * @return {Promise}
 */
export const Login = (params: ILogin): Promise<IResponse> => {
  return axiosInstance.post('user/login',params).then(res => res.data);
};

/**
 * @description: 通过id获取用户
 * @params {IUser} params
 * @return {Promise}
 */
export const getUserInfo = (params: IUser): Promise<IResponse> => {
  return axiosInstance.post('user/getInfo',params).then(res => res.data);
};
  • 本文件创建 axios 实例,通过给实例添加请求和响应拦截,做对应处理。
  • axios.create 中 transformRequest 配置,先删除参数中的 Authorization, 再通过 qs (npm install @types/qs) 依赖转换了参数的形式(将对象字面量转化成 Form Data 格式)。
  • 响应拦截中做了对 token 的处理
  • 请求拦截中在请求头部 header 中给 Authorization 添加 token,根据具体业务需求修改。
  • 本文件创建一个登录接口 login 和获取用户信息的接口 getUserInfo 用来测试 eggjs 提供的接口。
  • 为了统一处理,所有请求均使用 post 方式

./src/api/status.ts

export const showMessage = (status:number|string) : string => {
  let message:string = "";
  switch (status) {
    case 400:
      message = "请求错误(400)";
      break;
    case 401:
      message = "未授权,请重新登录(401)";
      break;
    case 403:
      message = "拒绝访问(403)";
      break;
    case 404:
      message = "请求出错(404)";
      break;
    case 408:
      message = "请求超时(408)";
      break;
    case 500:
      message = "服务器错误(500)";
      break;
    case 501:
      message = "服务未实现(501)";
      break;
    case 502:
      message = "网络错误(502)";
      break;
    case 503:
      message = "服务不可用(503)";
      break;
    case 504:
      message = "网络超时(504)";
      break;
    case 505:
      message = "HTTP版本不受支持(505)";
      break;
    default:
      message = `连接出错(${status})!`;
  }
  return `${message},请检查网络或联系管理员!`;
};
  • status.ts 返回相应状态对应的信息文字。

./src/model/login.ts

export interface ILogin {
  username: string;
  password: string;
}

export const createEmptyLogin = (): ILogin => ({
  username: "",
  password: ""
});

export interface IUser {
  userId: string;
}
  • 这里集中定义各种接口相关的 interface。

测试登录接口,只列出主要逻辑:

...
import { ILogin } from "model/login";
import { IResponse, Login } from 'api/api';
import { Button, message } from 'antd';
...
export default () => {
  ...
  // 登录按钮逻辑
  const handleLogin = async (login:ILogin) => {
    // 调用登录Api,获取结果
    let res:IResponse = await Login(login);
    // 处理结果
    if( res.code === 200 ){
      if(res.data.user_id){
        localStorage.setItem('user_id', res.data.user_id);
      }
      // 提示登录成功
      message.success(res.msg);
    }else{
      // 提示登录失败
      message.error(res.msg);
    }
  };
  ...
  return (
    <>
      <Button 
        type="ghost" 
        onClick={() => handleLogin({username:'admin',password:'123456'})}
      >登陆</Button>
    </>
  )
}

用eggjs写个登录接口,返回的json结构是这样。

{
  "code":200,
  "msg":"登录成功",
  "data":{
    "user_id": "57c4dda0b42521e3994aa5196ae79485",
    "token": "1yJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.2yJ1c2VybmFtZSI6InNvbmljIiwiaWFwIjoxNjE5NDkxMzIzfQ.uIlcV-hO2FZgskyBorMfoUi2tN69MU51XXEbtMN2BZQ"
  }
}
  • 登录成功后将 user_id 存入本地存储。
  • 登录接口请求头部不带 authorization ,登录成功后将 token 存入本地存储, ./src/api/api.ts 中做了这样的处理。

再看获取用户信息的接口。

import React,{ useState, useEffect } from "react";
import { Button } from 'antd';
import { IResponse, getUserInfo } from 'api/api';
import { IUser } from "model/login";
export default () => {

  const [user, setUser] = useState<IUser>({ userId: String(localStorage.getItem('user_id')) || "" });
  const [userInfo, setUserInfo] = useState<any>(null);

  // 获取用户信息
  const showUserInfo = async (user:IUser) => {
  	let res = await getUserInfo(user);
  	console.log(res);
  	setUserInfo(res.data);
  }

  // 组件加载完后执行
  useEffect(()=>{
    // 调用获取用户信息接口
  	showUserInfo(user);
  },[]);

  return(
  	<>
  	  <Button 
	    style={{'width':'200px'}}
	    type="primary" 
	    onClick={() => showUserInfo(user)}
	  >获取用户信息</Button>
	  <p>{JSON.stringify(userInfo)}</p>
  	</>
  )
}
  • useEffect 当作 componentDidMount 生命周期使用,里面的逻辑是页面默认调用获取用户信息接口。
  • 点击按钮也可调用一次接口。

获取用户信息返回的json结构

{
  "code":200,
  "msg":"获取用户信息成功",
  "data":{
    "user_id": "57c4dda0b42521e3994aa5196ae79485",
    "name": "admin",
    "real_name": "张一",
    "user_role":2,
    "headimg": null,
    "login_time": 1564994793122,
  }
}

最后几点