使用Refit 6.0.94时Linux容器中Post/Patch请求抛出System.Text.Json.JsonReaderException问题求助
这个问题我之前遇到过类似的情况,大概率是API返回的响应不是合法JSON导致的——错误里的'M'很可能是"Method Not Allowed"这类文本响应的开头,而Refit默认期望解析JSON格式的响应,所以抛出了解析异常。结合你的场景(本地正常、容器里只有Patch/Post出错),可以按以下步骤排查和解决:
第一步:先确认API返回的原始内容
在你的Blazor代码的catch块里,先把ApiException的原始响应内容打印出来(比如写入日志或者临时显示在页面上),这是定位问题的关键:
catch (ApiException ex) { // 先记录原始响应内容 var rawResponseContent = ex.Content; Console.WriteLine($"Raw API Response: {rawResponseContent}"); try { var errors = await ex.GetContentAsAsync<Dictionary<string, string>>(); var message = string.Join("; ", errors.Values); throw new Exception(message); } catch { // 如果解析JSON失败,直接抛出原始响应内容 throw new Exception($"API Error: {rawResponseContent}"); } }
如果输出是类似Method Not Allowed的文本,那问题就出在路由不匹配或者API端点未正确接收Patch/Post请求。
第二步:检查路由匹配一致性
你的Refit接口里,Patch/Post的路由是[Patch("/CustomerCare/")]和[Post("/CustomerCare/")](末尾带斜杠),而API的Patch方法是[HttpPatch](没有指定路由)。结合BaseAddress是http://myservername/api/v1,最终的请求路径是http://myservername/api/v1/CustomerCare/。
需要确认API控制器的路由配置:比如你的CustomerCareController是不是有类似这样的路由?
[ApiController] [Route("api/v1/[controller]")] // 这里的路由是api/v1/CustomerCare public class CustomerCareController : ControllerBase { [HttpPatch] public async Task<IActionResult> PatchAsync([FromBody] Customer customer) { // ... } }
如果是这样,API的Patch端点路径是api/v1/CustomerCare(不带末尾斜杠),而Refit请求的是api/v1/CustomerCare/——末尾多了个斜杠会导致路由不匹配,API返回405 Method Not Allowed(文本响应),这就是你看到的'M'开头错误的来源。
解决方案:统一路由格式,去掉Refit接口里路由的末尾斜杠:
public interface ICustomerService { [Delete("/CustomerCare")] // 同步修改Delete的路由,保持一致性 Task<bool> DeleteCustomer(int id); [Patch("/CustomerCare")] Task<bool> PatchCustomerAsync([Body] Customer customer); [Get("/CustomerCare/{id}")] Task<Customer> GetCustomerByIdAsync(int id); [Post("/CustomerCare")] Task PostAsync([Body] Customer customer); }
第三步:确保API错误响应也是JSON格式
如果API在出现异常时返回的是HTML错误页面(比如默认的ASP.NET错误页),也会导致Refit解析JSON失败。建议给API配置全局异常处理,确保所有错误都返回JSON格式:
// 在API的Startup.cs/Program.cs中添加全局异常处理中间件 app.UseExceptionHandler(errorApp => { errorApp.Run(async context => { context.Response.ContentType = "application/json"; var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); var exception = exceptionHandlerPathFeature.Error; var errorResponse = new { Message = exception.Message, StatusCode = StatusCodes.Status500InternalServerError }; await context.Response.WriteAsJsonAsync(errorResponse); }); });
第四步:验证Refit的序列化配置(可选)
虽然本地正常,但可以显式指定Refit使用System.Text.Json序列化,确保容器环境下的一致性:
services.AddRefitClient<ICustomerService>(new RefitSettings { ContentSerializer = new SystemTextJsonContentSerializer(new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 匹配你的POCO类配置,比如支持可空DateTime Converters = { new JsonStringEnumConverter() } }) }).ConfigureHttpClient(c => c.BaseAddress = new Uri("http://myservername/api/v1"));
按以上步骤排查,大概率能解决你的问题。
内容的提问来源于stack exchange,提问作者BankMan101

