umi-request使用

4,614 阅读5分钟

umi-request的使用相对比较简单, 甚至可以直接使用,由于其支持 typescript,再加上其使用简单,因此非常受大家的喜爱,下面就简单介绍下其使用方式吧

快速使用

下面就是最简单的使用方法,直接引入 request,直接使用即可

import request from "umi-request";

export function loginByInfos(
    params: API.LoginParams,
    options?: { [key: string]: any },
) {
    //后面的泛型,表示返回数据 reponse 的数据类型,避免编译期间无法调用
    return request<API.BaseResponse<any>>('/street_light/api/user/login', {
        //这里面有常见的参数,包括timeout等,仅用于本次请求设置,对全局没有影响
        method: 'POST',
        data: params,
        requestType: 'form', //post request data type
        ...(options || {}),
    });
}

在呈上简化后的类型结构,实际可以根据类型自己更新,声明命名空间,可以保证里面类型即使与其他文件一直,只要命名空间变量不重复就没事任何事情

//声明类型,我们使用命名空间,避免类型冲突
declare namespace API {
    type BaseResponse<T> = {
        code: number
        data: T | null
        message: string
    }

    type LoginParams = {
        account: string;
        password: string;
    };

    type DeviceListParams = {
        device_name?: string //设备名字搜索
        page_num: number  //1~more
        page_size: number //1~more
    }
}

全局请求设置

平时我们使用的时候,如果是 post 一般使用的 content-typeapplication/x-www-form-urlencoded,其以 key-value 的形式传递,非常万能,深受大家喜爱,这里只需要将全局的 requestType设置成 form即可,我们 redentials: 'include'表示携带 cookie,一般用的比较多的就这些了,还有需要的可以点进去看看其他属性

import {extend} from "umi-request";

export const request = extend({
    timeout: 10000,
    requestType: 'form',
    credentials: 'include' //携带cookie
})

如果想在局部中改变 requestType 或者 timeout,那么直接直接写到request第二个参数里面即可

request('url', {
    //这里面有常见的参数,包括timeout等,仅用于本次请求设置,对全局没有影响
    method: 'POST',
    data: params,
    requestType: 'form', //post request data type
    ...(options || {}),
});

全局请求设置 + 请求拦截器

全局请求设置不多说,如果写过其他编程语言可能会对请求拦截器有所体会,这里的请求拦截器支持虽然不像其他语言一样优秀,但是也能帮助大家不少

import {extend} from "umi-request";

//全局请求参数设置
export const request = extend({
    timeout: 10000,
    requestType: 'form',
    //注意是抛出错误后的回调方法,后面告诉你为何不用他了,当然也可以根据需要加入
    // errorHandler,
    credentials: 'include'
})

//假设我们有一个token,登陆才能获取,要配合加密放到header中
let token = ''

//请求request请求数据拦截器,其发生在我们写的请求之后,实际发出请求之前
request.interceptors.request.use((url, options) => {
    //假设请求钱我们需要将一些令牌统一放到header中(根据实际需要处理)
    //将token和加密后的sign放到一起
    let headers: any = options.headers
    headers['token'] = token
    let sign = token + new Date().getTime()
    headers['sign'] = sign

    //设置完成后,要返回我们的的参数
    return {
        url,
        options: {
            ...options,
            headers
        }
    }
})

//返回数据 response 响应数据拦截器,可以拦截一些常见的状态码错误
//其发生在实际请求之后,到给我们返回的Promise之前

//根据案例演示,再加上实际使用,这个框架应用上并不是那么便利,一般仅仅用来提示打印弹窗
//我们可以稍微改进,对我们成功的进行处理,失败的给出promise
//这样状态码不对的会走到请求的.catch当中和我们的基础结构很像
request.interceptors.response.use(async (response, options) => {
    console.log('response', response)
    const {url, status} = response
    //保证请求成功,可以再 401 登录过期时,清理用户缓存以及token等信息,并弹窗提示重新登录
    if (status === 401) {
        //我们可以通过 url 检查 token,赋值刷新token重新请求 token 问题,例如双token刷新
        //可以给我们的请求设置 getResponse: true 就可以同时返回data和response了,可以用与更新我们的请求结果
        ...
    }else if (status === 200) {
        //我们可以直接检查返回数据内容,根据需要检查即可,一般用来检查是否成功
        let data = await response.clone().json()
        
    }
    return response;
})

也简单上一下 errorHandler 的吧,这里面可以根据状态码来提示错误信息,但不建议过多使用,当服务器出现错误时,这个 error 会返回一个结构异常的 error 信息,直接以其原来结构会出错,问题挺多的

function errorHandler(error: ResponseError) {
    //可以在这里处理状态码的问题,一般打印或者弹窗(弹窗有时候也很烦,如果多个不同请求,出现不同错误,就弹会同时弹出多个,自己可以看情况处理)
    if (error.response.status === 401) {
        console.log(error)
        message.showError(error.message)
        throw error
    }
}

上面也介绍了,request 的拦截器还是比较好用的,response 的拦截器比较鸡肋,如果是正常的状态码没问题,如果是错误的,会自动走到errorhandle中,并且只能系统默认错误和我们reject的会进入errorhandle,这样导致 errorhandle 中的结构很是不稳定,且我们无法修改,并且一旦出现 404 或者报错之类的,会直接 errorhandle中参数都是异常的(且跟三方声明的结构不一样,挺鸡肋的),因此很不推荐

可以向我们上面一样,默认直接返回 response,请求成功,但是数据不对我们直接 reject,这样会走到外面的 .catch 中,我们可以统一处理,包括特殊的状态码等信息

使用一下看看,这样就可以了

import { request } from "./index"

//设置我们的请求吧,来个 post的
//此时拦截后,除了系统的默认错误,我们也自己 reject 返回了一个错误信息,可以查看一下看
export function loginByInfos(
    params: API.LoginParams,
    options?: { [key: string]: any },
) {
    return request<API.BaseResponse<any>>('/url', {
        method: 'POST',
        data: params,
        requestType: 'json', //全局设置了form,如果想传递 json 就单独传递就行了
        ...(options || {}),
    });
}

//来个 get的
export function getDeviceList(
    params?: API.DeviceListParams, //声明好自己的类型
    options?: { [key: string]: any }
) {
    //response根据自己的类型写上,以在编辑期间合理使用
    return request<API.BaseResponse<any>>('/url', {
        method: 'GET',
        params,
        ...(options || {})
    })
}