最新消息:20210816 当前crifan.com域名已被污染,为防止失联,请关注(页面右下角的)公众号

【已解决】reactjs的Antd Pro中如何让网络异常出错时也有callback返回可以自己控制异常

JS crifan 962浏览 0评论
折腾:
【未解决】Antd Pro中点击提交后禁止再点击按钮
期间,现有的Reactjs的Antd Pro中的相关网络请求的代码是:
          this.props.dispatch({
            type: 'script/submitScript',
            payload: params,
          }).then(() => {
            this.props.dispatch(routerRedux.push('/script/script-list'));
          });
->
src/models/script.js
    *submitScript({ payload }, { call }) {
      yield call(createScript, payload);
      message.success('新建剧本成功');
    },
->
src/services/api.js
export async function createScript(params) {
  return request(`${apiPrefix}/scripts/`, {
    method: 'POST',
    body: params,
    headers: constructHeaders(),
  });
}
->
src/utils/request.js
export default function request(url, options) {
  const defaultOptions = {
    credentials: 'include',
  };
  const newOptions = { ...defaultOptions, ...options };
  if (newOptions.method === 'POST' || newOptions.method === 'PUT' || newOptions.method === 'PATCH') {
    if (!(newOptions.body instanceof FormData)) {
      newOptions.headers = {
        Accept: 'application/json',
        'Content-Type': 'application/json; charset=utf-8',
        ...newOptions.headers,
      };
      newOptions.body = JSON.stringify(newOptions.body);
    } else {
      // newOptions.body is FormData
      newOptions.headers = {
        Accept: 'application/json',
        ...newOptions.headers,
      };
    }
  }

  return fetch(url, newOptions)
    // .then(debugResponse)
    .then(checkStatus)
    .then(response => {
      if (newOptions.method === 'DELETE' || response.status === 204) {
        return response.text();
      }
      if (url.includes('word')) {
        window.open(url);
      }
      return response.json();
    })
    .catch(e => {
      console.log(`url=${url} -> error=`, e);

      const { dispatch } = store;
      const status = e.name;
      if (status === 401) {
        dispatch({
          type: 'login/logout',
        });
        return;
      }
      if (status === 403) {
        dispatch(routerRedux.push('/exception/403'));
        return;
      }
      if (status <= 504 && status >= 500) {
        dispatch(routerRedux.push('/exception/500'));
        return;
      }
      if (status >= 404 && status < 422) {
        dispatch(routerRedux.push('/exception/404'));
      }
    });
}
很明显,此处的http的request,当出错时,最终都被catch中去统一处理掉异常了
从而没有让fetch的调用者直到异常,也就无法自己掌控
比如此处,希望在异常时,去掉之前按钮的disable,允许重新被点击
所以此处,目前能想到的,最完美的情况是:
fetch调用后台api返回后,不论是exception出错,还是正常response返回,都有个callback
而此处的调用接口时,都是dispatch一个字符串
然后跳转到负责的model去处理的
现在正常返回是有callback的
所以要想办法去在现有基础上加个exception的callback
antd pro api 异常 回调函数
Ant Design Pro 开发记录 – CSDN博客
this.props.dispatch({
    type: '***',
    payload: '***',
    callback: () => {
        // callback function
    }
})
去找找此处是否真的支持callback
才发现是自己去添加的:
*fetch({ payload, callback }, { call, put }) {
    // request here
    if (callback) callback() // ** 回调函数 **
})
effects调用reducers更改状态,页面回调函数无法获取到 · Issue #914 · ant-design/ant-design-pro
使用 dva 进行数据操作请求之后,如何优雅的处理服务器端返回来的数据? · Issue #930 · ant-design/ant-design-pro
试试直接传递callback会发生什么
        if (this.isEditMode()){
          dispatchDict = {
            type: 'script/updateScript',
            payload: params,
            scriptID: this.state.curScriptId,
            callback: (resp) => {
              console.log("updateScript callback: resp=", resp)
            }
          }
        } else {
          dispatchDict = {
            type: 'script/submitScript',
            payload: params,
            callback: (resp) => {
              console.log("submitScript callback: resp=", resp)
            }
          }
        }
        console.log("dispatchDict=", dispatchDict)
        this.props.dispatch(dispatchDict).then(() => {
          this.props.dispatch(routerRedux.push('/script/script-list'));
        })
当然没有任何反应,毕竟接口不支持。
去添加支持errorCallback
【总结】
最后采用的是:
虽然不完美,但是凑合可以达到基本的效果:
不论是返回正常数据还是出错,都可以回调函数的方式:
src/routes/Script/ScriptCreateEdit.js
      params.dialogs = dialogItemList
      console.log('params=', params)

      const okOrErrorCallback = (errOrData) => {
        console.log("okOrErrorCallback: errOrData=", errOrData)

        if(!errOrData) {
          console.log("okOrErrorCallback: Omit response empty")
          return
        }

        this.setState({
          isSubmitting: false,
          isDisableSubmit: false,
        })
        console.log(`okOrErrorCallback: isSubmitting=${this.state.isSubmitting},isDisableSubmit=${this.state.isDisableSubmit}`)

        if(errOrData instanceof Error) {
          const errMsg = this.state.curPageTitle + "出错:" + `${errOrData}`
          console.log(errMsg)
          message.error(errMsg)
        } else {
          const okMsg = this.state.curPageTitle + "成功"
          console.info(okMsg)
          message.info(okMsg)

          this.props.dispatch(routerRedux.push('/script/script-list'))
        }
      }

      let dispatchDict
      if (this.isEditMode()){
        dispatchDict = {
          type: 'script/updateScript',
          payload: params,
          scriptID: this.state.curScriptId,
          okOrErrorCallback: okOrErrorCallback,
        }
      } else {
        dispatchDict = {
          type: 'script/submitScript',
          payload: params,
          okOrErrorCallback: okOrErrorCallback,
        }
      }
      console.log("dispatchDict=", dispatchDict)

      this.setState({
        isSubmitting: true,
        isDisableSubmit: true,
      })
      console.log(`Now will submit: isSubmitting=${this.state.isSubmitting},isDisableSubmit=${this.state.isDisableSubmit}`)

      // const afterSubmitCallback = (resp) => {
      //   console.log("afterSubmitCallback: resp=", resp)
      // }
      // this.props.dispatch(dispatchDict).then(afterSubmitCallback())
      this.props.dispatch(dispatchDict)
src/models/script.js
    *submitScript({ payload, okOrErrorCallback}, { call }) {
      console.log("submitScript: payload=", payload, ", okOrErrorCallback=", okOrErrorCallback)
      const resp = yield call(createScript, payload, okOrErrorCallback)
      console.log("after createScript: resp=", resp)
      if(okOrErrorCallback) {
        okOrErrorCallback(resp)
      }
    },
    *updateScript({ payload, scriptID, okOrErrorCallback}, { call }) {
      console.log("updateScript: payload=", payload, ", scriptID=", scriptID, ", errorCallback=", okOrErrorCallback)
      const resp = yield call(updateScript, payload, scriptID);
      console.log("after updateScript: resp=", resp)
      if(okOrErrorCallback) {
        okOrErrorCallback(resp)
      }
    },
src/services/api.js

export async function createScript(params, okOrErrorCallback) {
  console.log("createScript: params=", params, ", okOrErrorCallback=", okOrErrorCallback)

  return request(
    `${apiPrefix}/scripts/`,
    {
      method: 'POST',
      body: params,
      headers: constructHeaders(),
    },
    okOrErrorCallback
  )
}

export async function updateScript(params, scriptID, okOrErrorCallback) {
  return request(
    `${apiPrefix}/scripts/${scriptID}/`,
    {
      method: 'PUT',
      body: params,
      headers: constructHeaders(),
    },
    okOrErrorCallback
  )
}
src/utils/request.js
import fetch from 'dva/fetch';
import { notification } from 'antd';
import { routerRedux } from 'dva/router';
import store from '../index';

const codeMessage = {
  200: '服务器成功返回请求的数据。',
  201: '新建或修改数据成功。',
  202: '一个请求已经进入后台排队(异步任务)。',
  204: '删除数据成功。',
  400: '发出的请求有错误,服务器没有进行新建或修改数据的操作。',
  401: '用户没有权限(令牌、用户名、密码错误)。',
  403: '用户得到授权,但是访问是被禁止的。',
  404: '发出的请求针对的是不存在的记录,服务器没有进行操作。',
  406: '请求的格式不可得。',
  410: '请求的资源被永久删除,且不会再得到的。',
  422: '当创建一个对象时,发生一个验证错误。',
  500: '服务器发生错误,请检查服务器。',
  502: '网关错误。',
  503: '服务不可用,服务器暂时过载或维护。',
  504: '网关超时。',
};

function debugResponse(response){
  response.json().then(
    body => {
      console.log(`debugResponse`)
      console.log(`typeof body=`, typeof body)
      console.log(`body=`, body)
      console.log("JSON.stringify(body)=",JSON.stringify(body))
    }
  )

  return response
}


// function noticeErrorInfo(response, respJson, okOrErrorCallback) {
function noticeErrorInfo(response, okOrErrorCallback) {
  console.log("noticeErrorInfo: response=", response, ", okOrErrorCallback=", okOrErrorCallback)
  console.log(`typeof response=`, typeof response)
  console.log("response.status=", response.status)
  console.log("response.url=", response.url)

  response.json().then(
    respJson => {
      console.log("response.json().then: respJson=", respJson)
      console.log(`typeof respJson=`, typeof respJson)
      console.log("JSON.stringify(respJson)=",JSON.stringify(respJson))

      let respMessage
      if ("message" in respJson) {
        respMessage = respJson.message
        console.log(`respMessage=`, respMessage)
      } else {
        console.log("message not in response body")
      }

      const errorText = respMessage || codeMessage[response.status] || response.statusText
      console.log("errorText=", errorText)

      notification.error({
        message: errorText,
        description: `请求 ${response.url} 出错  ${response.status}`,
      })

      const reqUrlError = new Error(errorText)
      reqUrlError.name = response.status
      reqUrlError.response = response
      console.log("reqUrlError=", reqUrlError)
      // throw reqUrlError
      // return reqUrlError

      if(okOrErrorCallback) {
        okOrErrorCallback(reqUrlError)
        // console.log("return reqUrlError=", reqUrlError)
        // return reqUrlError
      }
    })
}

/**
 * Requests a URL, returning a promise.
 *
 * @param  {string} url       The URL we want to request
 * @param  {object} [options] The options we want to pass to "fetch"
 * @return {object}           An object containing either "data" or "err"
 */
export default function request(url, options, okOrErrorCallback) {
  console.log(`request: url=${url}, options=`, options, ", okOrErrorCallback=", okOrErrorCallback)

  const defaultOptions = {
    credentials: 'include',
  };
  const newOptions = { ...defaultOptions, ...options };
  if (newOptions.method === 'POST' || newOptions.method === 'PUT' || newOptions.method === 'PATCH') {
    if (!(newOptions.body instanceof FormData)) {
      newOptions.headers = {
        Accept: 'application/json',
        'Content-Type': 'application/json; charset=utf-8',
        ...newOptions.headers,
      };
      newOptions.body = JSON.stringify(newOptions.body);
    } else {
      // newOptions.body is FormData
      newOptions.headers = {
        Accept: 'application/json',
        ...newOptions.headers,
      };
    }
  }

  return fetch(url, newOptions)
    // .then(debugResponse)
    .then(response => {
      console.log("fetch response=", response, ",newOptions=", newOptions)

      if (newOptions.method === 'DELETE' || response.status === 204) {
        return response.text()
      }

      if (response.status >= 200 && response.status < 300) {
        console.log("ok -> response.status=", response.status)
        return response.json()
      }

      // response.json().then(
      //   respJson => {
      //     noticeErrorInfo(response, respJson, okOrErrorCallback)
      //   }
      // )
      return noticeErrorInfo(response, okOrErrorCallback)
    })
    .catch(err => {
      console.error(`catch: url=${url} -> error=`, err)

      const errorText = `解析请求${url}的返回时出错:${err}`
      console.log("errorText=", errorText)
      const parseRespError = new Error(errorText)
      console.log("parseRespError=", parseRespError)
      if(okOrErrorCallback) {
        console.log("return parseRespError=", parseRespError)
        okOrErrorCallback(parseRespError)
      }

      // if (okOrErrorCallback) {
      //   okOrErrorCallback(err)
      // } else {
      //   const { dispatch } = store;
      //   const status = err.name
      //   if (status === 401) {
      //     dispatch({
      //       type: 'login/logout',
      //     });
      //     return;
      //   }
      //   if (status === 403) {
      //     dispatch(routerRedux.push('/exception/403'));
      //     return;
      //   }
      //   if (status <= 504 && status >= 500) {
      //     dispatch(routerRedux.push('/exception/500'));
      //     return;
      //   }
      //   if (status >= 404 && status < 422) {
      //     dispatch(routerRedux.push('/exception/404'));
      //   }
      // }
    });
}
基本上实现了:
如果是出错,则不论是request的url的error,还是response的解析为text或json等的error,都会调用回调函数okOrErrorCallback,然后可以在okOrErrorCallback中写正常和出错的处理的逻辑:
(1)出错情况:
此处去删除本地local storage中token:
模拟没有登录,去调试401没权限的效果是:
以及:
断开localhost的server,模拟网络断开的效果:
(2)正常情况:

转载请注明:在路上 » 【已解决】reactjs的Antd Pro中如何让网络异常出错时也有callback返回可以自己控制异常

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
82 queries in 0.183 seconds, using 22.20MB memory