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

POST请求后XMLHttpRequest无法获取服务器返回的错误状态

嵌入式Web服务器固件上传错误状态无法获取问题

我在一款小型单片机上开发嵌入式Web服务器,已实现网页服务等功能,其中包含固件上传页面。该功能正常工作,但用户上传错误文件(如扩展名不符)时,虽能触发前端error事件提示上传失败,却无法获取服务器返回的错误状态——失败时status始终为0(成功时正常返回200),responseText为空。服务器已返回HTTP/1.1 400 Bad Request: Invalid argument (File Extension Invalid),且onreadystatechange中readyState能达到4,但status仍为0。

以下是固件上传页面的代码:

var downloadTimer;
var ajax;

function _(el) {
  return document.getElementById(el);
}

function uploadFile() {
  var file = _("file1").files[0];
  var formdata = new FormData();
  formdata.append("file1", file);
  ajax = new XMLHttpRequest();
  ajax.upload.addEventListener("progress", progressHandler, false);
  ajax.addEventListener("load", completeHandler, false);
  ajax.addEventListener("error", errorHandler, false);
  ajax.addEventListener("abort", abortHandler, false);
  _("progressBar").style.visibility = 'visible';

  ajax.onreadystatechange = function() {
    if ((this.readyState === 4) && (this.status > 0)){
      console.log(this.responseText);
    }
  }

  ajax.open("POST", "/upload.cgi");
  var timeleft = 90;
  uploadTimer = setInterval(function(){
    if (timeleft <= 0){
      clearInterval(downloadTimer);
      _("progressBar").style.visibility = 'hidden';
      _("loaded_n_total").innerHTML = "";
      _("status").innerHTML = "Firmware Update Complete";
      clearInterval(downloadTimer);
    } else {
      var percent = Math.round((90 - timeleft) * 1.111);
      _("progressBar").value = percent;
          _("status").innerHTML = Math.round(percent) + "% uploaded... please wait";
      timeleft -= 1;
    }
  }, 1000);
  ajax.send(formdata);
}

function progressHandler(event) {
  _("loaded_n_total").innerHTML = "Uploaded " + event.loaded + " bytes of " + event.total;
}

function completeHandler(event) {
  _("loaded_n_total").innerHTML = "Applying new Firmware...please wait";
  
}

function errorHandler(event) {
  clearInterval(uploadTimer);
  _("progressBar").style.visibility = 'hidden';
  _("loaded_n_total").innerHTML = "";
    _("status").innerHTML = "Upload Failed";
}

function abortHandler(event) {
  clearInterval(uploadTimer);
  _("progressBar").style.visibility = 'hidden';
  _("loaded_n_total").innerHTML = "";
    _("status").innerHTML = "Upload Aborted";
} 

问题分析与解决方案

核心原因

当XMLHttpRequest请求遇到网络级错误或服务器返回的响应不符合HTTP规范时,浏览器会标记请求为error,此时status会被置为0,即便服务器实际返回了状态码。嵌入式Web服务器常出现这类问题:

  • 服务器返回的HTTP响应头不完整(比如缺少Content-Length或Connection头)
  • 响应体和头的格式不符合标准(比如换行符使用错误,或头字段格式不规范)
  • 服务器在返回错误后立即关闭连接,导致浏览器无法正确解析响应

修复步骤

1. 修正服务器的HTTP响应格式

确保返回400错误时,响应完全符合HTTP/1.1规范,示例格式如下:

HTTP/1.1 400 Bad Request
Content-Type: text/plain
Content-Length: 55
Connection: close

Invalid argument (File Extension Invalid)
  • 必须包含Content-Length,明确响应体的字节数
  • 头和响应体之间必须有两个连续的换行符(\r\n\r\n)
  • 避免返回响应后立即强制关闭TCP连接,让连接正常完成收尾

2. 前端代码调整:改用loadend事件统一处理结果

error事件触发时浏览器可能已丢弃响应数据,改用loadend事件可以在请求结束后统一处理,同时尝试读取status和responseText:

// 移除原有的load、error事件监听,替换为loadend
ajax.addEventListener("loadend", loadendHandler, false);

function loadendHandler(event) {
  clearInterval(uploadTimer);
  _("progressBar").style.visibility = 'hidden';
  _("loaded_n_total").innerHTML = "";

  if (ajax.status === 200) {
    _("status").innerHTML = "Applying new Firmware...please wait";
  } else if (ajax.status === 400) {
    _("status").innerHTML = "Upload Failed: " + ajax.responseText;
  } else {
    // 处理status为0的情况,尝试读取responseText
    if (ajax.responseText) {
      _("status").innerHTML = "Upload Failed: " + ajax.responseText;
    } else {
      _("status").innerHTML = "Upload Failed";
    }
  }
}

同时删除原有的completeHandler和errorHandler,统一在loadendHandler中处理所有结束状态。

3. 移除模拟进度的定时器(推荐)

当前代码用定时器模拟上传进度,和实际的progress事件冲突,建议完全依赖progress事件更新进度:

// 删除原有的uploadTimer相关代码
// 修改progressHandler:
function progressHandler(event) {
  if (event.lengthComputable) {
    var percent = Math.round((event.loaded / event.total) * 100);
    _("progressBar").value = percent;
    _("status").innerHTML = percent + "% uploaded... please wait";
    _("loaded_n_total").innerHTML = "Uploaded " + event.loaded + " bytes of " + event.total;
  }
}

额外调试技巧

  • 使用浏览器开发者工具的Network面板,查看请求的响应详情:如果响应显示为“(failed)”或“cancelled”,说明服务器端响应存在格式问题
  • 在服务器端打印完整的响应内容,检查是否符合HTTP规范

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 17:40:09