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
相关产品推荐
相关产品推荐

