You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

fetch处理服务端响应及400状态码自定义错误实现方案

问题核心需求
  • 成功响应场景:服务端返回2xx状态码无错误时,提取返回数据的指定字段,执行后续业务逻辑
  • 错误响应场景:服务端返回400等错误状态码、携带自定义错误说明时,按状态码捕获错误,提取响应体中data.error字段的错误文本,完成日志打印、DOM提示等操作
原有代码问题点
  • DOM选择器错误:用户名输入框的jQuery选择器遗漏类名前缀.,写为$('usernameCon input'),无法匹配到对应输入框元素
  • Fetch状态判断逻辑完全错误:Fetch API 仅在网络故障、请求被中断等场景下会触发Promise reject进入catch,服务端返回400/500等HTTP错误状态时,Promise默认是成功状态,不会进入catch,因此原代码在catch中读取error.responseStatus永远拿不到值
  • Promise语法错误:.then回调中直接跟if语句未包裹花括号,catch分支内错误嵌套无效箭头函数,不符合JS语法规范
  • 后端接口响应写法错误:用户保存失败分支写为res.send(400).send(err),Express中设置HTTP状态码必须调用res.status(code),原写法会导致状态码设置失效、响应格式异常
修正后代码

前端代码

$('.sign_upBtn').on('click', async function() {
  const name = $('.nameCon input').val()
  // 修正选择器,补上类名前缀.
  const username = $('.usernameCon input').val()
  const password = $('.passwordCon input').val()

  try {
    const res = await fetch('/api/user/register', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        name: name,
        username: username,
        password: password
      })
    })
    // 无论成功失败,先解析响应体JSON
    const respData = await res.json()
    // 手动判断HTTP状态码,非2xx状态统一抛错
    if (!res.ok) {
      const err = new Error('request failed')
      err.status = res.status
      err.data = respData
      throw err
    }

    // 走到此处即为请求成功,执行正常业务逻辑
    console.log('注册成功,返回用户数据:', respData)
    if (respData.name === 'john') {
      console.log(respData)
      // 其他自定义业务逻辑可写在此处
    }
  } catch (error) {
    // 统一处理所有异常:网络错误、服务端返回的4xx/5xx错误
    if (error.status === 400) {
      const errMsg = error.data?.error
      console.log('400错误信息:', errMsg)
      if (errMsg === 'username already exists') {
        // DOM操作示例:页面提示用户名已占用
        $('.errTip').text('用户名已存在,请更换后重试')
      }
    } else {
      // 其他错误通用处理
      console.log('请求异常:', error)
      $('.errTip').text('注册失败,请稍后重试')
    }
  }
})

后端代码(Express)

router.post('/register', async(req, res) => {
  // 校验用户名是否已存在
  const usernameExist = await User.findOne({
    username: req.body.username
  })
  if (usernameExist) {
    return res.status(400).send({
      error: 'username already exists'
    })
  }

  // 创建新用户
  const user = new User({
    name: req.body.name,
    phone_number: req.body.phone_number,
    username: req.body.username,
    password: req.body.password
  })
  try {
    const savedUser = await user.save()
    // 创建成功返回201状态码更符合REST规范
    res.status(201).send(savedUser)
  } catch (err) {
    // 修正状态码设置方式,统一错误响应格式
    res.status(400).send({
      error: err.message || '用户创建失败'
    })
  }
})
关键逻辑说明
  • 禁止依赖catch分支捕获HTTP错误状态,必须在拿到Fetch返回的Response对象后,第一时间通过res.ok(等价于状态码范围200-299)判断请求是否成功
  • 非2xx状态的响应体必须正常通过res.json()解析,否则无法拿到服务端返回的自定义错误信息
  • 手动抛错时可以在Error对象上自定义挂载status、data属性,方便后续catch分支按状态码做差异化处理

内容的提问来源于stack exchange,提问作者seriously

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 14:06:14