为何期望JSON的Ajax请求在生产环境无法捕获HttpStatusCodeResult消息?
我有一个基于ASP.NET 4.6的Web API应用,接口在错误时返回HttpStatusCodeResult对象,成功时返回JSON对象:
public class CertificationController : Controller { public async Task<ActionResult> GetImagesAsync(string certFilter) { try { int imageCount = GetImagesCount(certFilter); if (imageCount == 0) { return new HttpStatusCodeResult(404, "No images to download"); } else if (imageCount > 100) { return new HttpStatusCodeResult(400, "Cannot download more than 100 images"); } else { string PathToImageZipfile = ZipImages(certFilter); return Json(new{zipfile=PathToImageZipfile}); } } catch(Exception e) { return new HttpStatusCodeResult(400, "Failed to download images"); } } }
前端通过以下Ajax调用该接口:
getImages(filtercert: string): any { notifierInstance.setNotifier(true, appConst.LoadingText); var certDataFilter = ko.toJSON(filtercert); return $.ajax({ url: "/certificate/getImagesAsync", data: '{"certfilter":"' + certDataFilter + '"}', type: "POST", dataType: "json", contentType: "application/json; charset=utf-8", async: true, beforeSend: function (xhr) { notifierInstance.setNotifier(true, appConst.downloadWaiting); }, success: function (data, textStatus, xhr) { window.open(data); notifierInstance.setNotifierSelfHide(appConst.downloadWaiting); }, error: function (xhr, status, error) { if (xhr.status == 400) { notifierInstance.setNotifierErrorSelfHideLongWait(error); } else if (xhr.status == 404) { notifierInstance.setNotifierWarningSelfHideLongWait(error); } else { notifierInstance.setNotifierError(appConst.ErrorLoadingText); } } }); }
该Ajax请求在预发布(Staging)及更低环境中能正常读取HttpStatusCodeResult中的自定义消息,但在生产环境中无法读取。已知原因是Ajax期望JSON类型响应,导致无法读取HttpStatusCodeResult的消息,但问题仅出现在生产环境。我尝试将生产代码部署到开发环境(连接开发数据库),但代码运行无异常,未复现问题。需要在开发/测试/预发布环境复现该生产问题,以说服主管API不能向期望JSON的Ajax请求返回HttpStatusCodeResult。
要在低环境复现生产环境的问题,核心是模拟生产环境的ASP.NET/IIS错误响应配置,具体步骤如下:
关闭Debug模式并启用Release编译
修改Web.config中的<compilation>节点,将debug设置为false,匹配生产环境的编译模式:<system.web> <compilation debug="false" targetFramework="4.6" /> </system.web>Debug模式下ASP.NET会返回更详细的错误信息,而Release模式会抑制部分调试内容,这是生产环境的默认配置。
启用ASP.NET自定义错误
在Web.config中开启<customErrors>节点,设置为生产环境常用的On模式:<system.web> <customErrors mode="On" defaultRedirect="~/Error" /> </system.web>当
customErrors设为On时,ASP.NET会替换原始的错误响应内容,不再返回你在HttpStatusCodeResult中定义的自定义消息,而是返回默认错误页面的内容,导致前端Ajax因无法解析JSON而获取不到预期的错误信息。启用IIS自定义错误(若部署在IIS)
如果生产环境是IIS部署,还需要配置<httpErrors>节点来模拟IIS的错误替换行为:<system.webServer> <httpErrors errorMode="Custom" existingResponse="Replace"> <remove statusCode="400" /> <remove statusCode="404" /> <error statusCode="400" path="/Error/BadRequest" responseMode="Redirect" /> <error statusCode="404" path="/Error/NotFound" responseMode="Redirect" /> </httpErrors> </system.webServer>该配置会让IIS替换ASP.NET返回的错误响应,前端收到的将是重定向后的页面内容而非原始的错误消息,完全匹配生产环境的行为。
验证响应内容类型
生产环境中,HttpStatusCodeResult返回的响应Content-Type通常是text/html或text/plain,而前端Ajax指定了dataType: "json",jQuery会尝试将非JSON内容解析为JSON,导致解析失败,error回调中的error参数会变成parsererror而非自定义消息。你可以在低环境通过F12开发者工具查看响应的Content-Type,确认是否和生产环境一致。
完成以上配置后,再次调用接口触发错误场景,就能复现生产环境中无法读取自定义错误消息的问题。
内容的提问来源于stack exchange,提问作者Anusha Bivera

