生产环境下ASP.NET Core MVC返回409时无法读取自定义错误消息
问题背景
搭建ASP.NET Core MVC服务器实现错误处理,本地环境返回4xx状态码时,客户端能正常读取自定义JSON错误信息;但部署到生产环境后,客户端调用response.json()时出现SyntaxError: Unexpected token T in JSON at position 0错误,无法读取自定义错误消息,而200成功响应则能正常读取展示。
控制器代码
[HttpPost] public JsonResult SaveRecord([FromBody]NewsLanguage newLanguage) { try { _context.NewsLanguages.Add(newLanguage); _context.SaveChanges(); } catch (Exception ex) { Console.WriteLine(ex.Message); Response.StatusCode = 409; string errMsg = ex.Message; if (ex.InnerException != null) errMsg = ex.InnerException.Message; return Json(new { status = "Error", message = errMsg }); } Response.StatusCode = 200; return Json(new { status = "success", message = "New News Language Saved Successfully!" }); }
前端fetch请求代码
try { const response = await submitForm("/saverecord", newsLanguage, "POST"); console.log(response); if (response.ok) handleResponse(response, newsLanguage); else { const err = await response.json(); throw new Error(err.message || err.statusText) } } catch (err) { console.log(err); handleErrorResponse(err, newsLanguage); } function submitForm(route, newsLanguage, method) { const requestOptions = { method: method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newsLanguage) }; return fetch(parurl + route, requestOptions); } async function handleResponse(response, newsLanguage, method) { const data = await response.json(); console.log(response, data) if (data.status === "success") { document.getElementById("ModalFormClose").click(); toastr.success(data.message, "PERLEWEB DATABASE INTERFACE"); if (method != "DELETE") { let table = $('#example').DataTable(); table.row.add({ "id": newsLanguage.Id, "languageName": newsLanguage.LanguageName }).draw(); } else { var table = $('#example').DataTable(); table.row($(this).parents('tr')).remove().draw(); } } else { toastr.error(response.responseJSON.message, "ERROR!") } } function handleErrorResponse(errorMsg) { toastr.error(errorMsg, "ERROR!") }
核心问题解答
1. 失败请求的错误消息去哪了?
你的自定义错误消息被生产环境服务器(如IIS)拦截,替换成了服务器默认的HTML错误页面。报错中的Unexpected token T就是因为响应内容是HTML(比如IIS默认409错误页开头为"Too..."),而非你返回的JSON数据。
2. 是否有办法显示该错误消息,还是只能返回HTTP响应状态码?
可以显示自定义错误消息,需要同时配置ASP.NET Core项目和生产服务器,确保4xx错误的自定义响应不会被拦截替换。
3. 为什么成功响应可以正常读取,错误响应却不行?
- 200成功响应属于正常状态码,生产服务器默认不会干预这类响应,你的JSON数据能直接传递到客户端。
- 4xx属于客户端错误状态码,生产服务器(如IIS)默认会自动替换这类响应为自身的错误页面,覆盖了你返回的自定义JSON。
4. 本地环境与生产环境的差异是否由服务器配置导致?
是。本地开发环境使用的Kestrel服务器默认不会拦截替换错误响应;而生产环境通常用IIS作为反向代理,IIS默认会处理4xx/5xx错误并返回自身页面,同时ASP.NET Core的生产环境配置也可能存在错误处理中间件的差异。
解决方案
1. 修改ASP.NET Core控制器代码
使用框架提供的IActionResult返回类型和对应状态码方法,替代直接设置Response.StatusCode,确保响应被正确处理:
[HttpPost] public IActionResult SaveRecord([FromBody]NewsLanguage newLanguage) { try { _context.NewsLanguages.Add(newLanguage); _context.SaveChanges(); return Ok(new { status = "success", message = "New News Language Saved Successfully!" }); } catch (Exception ex) { Console.WriteLine(ex.Message); string errMsg = ex.Message; if (ex.InnerException != null) errMsg = ex.InnerException.Message; // 使用Conflict方法返回409状态码和自定义JSON return Conflict(new { status = "Error", message = errMsg }); } }
2. 配置ASP.NET Core错误处理中间件
在Program.cs中添加配置,禁用默认的状态码页面替换,确保自定义响应能正常输出:
// 针对特定接口禁用状态码页面替换 app.UseWhen(context => context.Request.Path.StartsWithSegments("/saverecord"), appBuilder => { appBuilder.UseStatusCodePages(context => { return Task.CompletedTask; }); }); // 全局异常处理(可选,确保未捕获异常返回JSON) app.UseExceptionHandler(errorApp => { errorApp.Run(async context => { context.Response.StatusCode = StatusCodes.Status500InternalServerError; context.Response.ContentType = "application/json"; await context.Response.WriteAsJsonAsync(new { status = "Error", message = "服务器内部错误" }); }); });
3. 配置IIS(若部署在IIS上)
在项目的web.config中添加配置,让IIS保留ASP.NET Core返回的原始响应,不替换为自身错误页:
<system.webServer> <httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough" /> </system.webServer>
4. 前端代码容错处理
增加JSON解析失败的 fallback,避免因响应非JSON导致的报错:
else { try { const err = await response.json(); throw new Error(err.message || err.statusText); } catch (parseErr) { // 解析JSON失败时,读取响应文本作为错误信息 const errText = await response.text(); throw new Error(errText || `请求失败:${response.status}`); } }
内容的提问来源于stack exchange,提问作者d0rf47

